Compare commits

...

155 Commits

Author SHA1 Message Date
ab1ac7c995 Merge pull request #1875 from oqtane/master
Merge pull request #1874 from oqtane/dev
2021-12-12 20:46:54 -05:00
99b0c9c079 Merge pull request #1874 from oqtane/dev
3.0.1 release
2021-12-12 20:46:28 -05:00
e99ab431e1 Merge pull request #1872 from sbwalker/dev
create url mapping when page path changes
2021-12-12 09:50:33 -05:00
1e1aaaccca create url mapping when page path changes 2021-12-12 09:59:33 -05:00
318d6afb0e fix url mapping resx issue 2021-12-11 09:39:03 -05:00
ba353857eb Merge pull request #1869 from sbwalker/dev
fix url mapping resx issue
2021-12-11 09:30:19 -05:00
1fd42f343d Merge pull request #1867 from leigh-pointer/MissingRes-3.0.1
Missing resource Keys for URL mapping and Visitors
2021-12-11 09:25:45 -05:00
ec9686cfb8 Merge branch 'dev' into MissingRes-3.0.1 2021-12-11 09:25:03 -05:00
a1bff809f3 Merge pull request #1868 from sbwalker/dev
visitor improvements
2021-12-11 09:21:26 -05:00
76fe155c0a visitor improvements 2021-12-11 09:30:05 -05:00
0b0254aed9 Updated Resx file 2021-12-11 14:24:08 +01:00
9258c3849b Went through each Framework module updating Resources
New English resources added
2021-12-11 13:43:22 +01:00
d530f30bc9 Missing Res Keys
Added missing resource keys
2021-12-11 11:54:25 +01:00
7d8bbac04f remove quill 1.3.6 assets 2021-12-10 14:19:32 -05:00
7b0c0c3e17 prepare for 3.0.1 2021-12-10 14:16:16 -05:00
298c3097f7 Merge pull request #1866 from sbwalker/dev
remove quill 1.3.6 assets
2021-12-10 14:10:59 -05:00
3a3f221418 Merge pull request #1865 from sbwalker/dev
prepare for 3.0.1
2021-12-10 14:07:30 -05:00
78110791e1 Merge pull request #1863 from leigh-pointer/InstallManagerDelete
Fix for Installed packages not being removed correctly
2021-12-10 10:11:31 -05:00
95d4c3d0d5 Merge pull request #1864 from sbwalker/dev
adjust permissions for new settings
2021-12-10 10:11:16 -05:00
e95b49ba8f adjust permissions for new settings 2021-12-10 10:20:03 -05:00
92ccb7e463 Fix for Installed packages not being removed correctly
When a package is remove in some instance the system complains that a file still exists in the deleting directory but there is not file.
Added true parameter to the Directory delete for force the removal. 
Directory.Delete(Path.GetDirectoryName(filepath), true);
2021-12-10 16:06:12 +01:00
2f34bf69e3 moduledefinition settings and host settings 2021-12-09 15:50:00 -05:00
d093c03d92 Merge pull request #1862 from sbwalker/dev
moduledefinition settings and host settings
2021-12-09 15:41:19 -05:00
d1ade8789b Merge pull request #1832 from leigh-pointer/ModuleDefinitionSettings
Settings for ModuleDefinitions #1829
2021-12-09 13:35:54 -05:00
1291eb5b7c Merge pull request #1861 from sbwalker/dev
added support for url mapping and viitors
2021-12-09 08:40:15 -05:00
9c32937c83 added support for url mapping and viitors 2021-12-09 08:48:56 -05:00
1ec28e9825 Merge pull request #1855 from svendu/fix_postgres_installation
Make IsPublic of type bool to make PostgreSQL happy
2021-12-08 12:54:30 -05:00
86fce898e5 Merge pull request #1853 from leigh-pointer/PagerBoth
Update the ToolBar position on the Pager Component
2021-12-08 12:54:10 -05:00
bbee87f7df Make IsPublic of type bool 2021-12-07 12:07:26 +01:00
811ddb9b44 Update the ToolBar position on the Pager Component
Add the option "Both" to display the toolbar at the top and bottom of the pager.  Nice if the Pager is displaying large sets of data.
2021-12-06 19:18:07 +01:00
de798da074 Merge pull request #1848 from leigh-pointer/RichTextContentRefresh
Fix #1837 RichTextEditor Content not re-Rendering
2021-12-03 09:45:59 -05:00
65d468be33 Fix #1837 RichTextEditor Content not re-Rendering
Change to the OnAfterRenderAsync method and changed OnInitialized to OnParametersSet
2021-12-03 06:31:45 +01:00
9664ff67f3 Merge pull request #1842 from leigh-pointer/QuillEditor1.3.7-SecurityUpdate
Quill Security related bug fixes.
2021-12-02 16:24:49 -05:00
99f73cf31e Merge pull request #1846 from sbwalker/dev
Additional properties added to Route model to improve abstraction, modified Site Settings to support settings moved to the server.
2021-12-02 16:24:39 -05:00
a216e2b92e Additional properties added to Route model to improve abstraction, modified Site Settings to support settings moved to the server. 2021-12-02 16:33:16 -05:00
9dfd9ad519 Quill Security related bug fixes.
Upgraded Quill references to 1.3.7
Tabnabbing vulnerability in snow theme #2438
https://github.com/quilljs/quill/issues/2438

https://github.com/quilljs/quill/releases/tag/v1.3.7
2021-12-02 09:56:55 +01:00
97133510a7 Update README.md 2021-12-01 09:11:21 -05:00
43d166fb7d Route parsing abstraction and optimization, site router performance improvements, migrate site-based concepts (favicon, PWA support) to server for performance and prerendering benefits, move ThemeBuilder interop logic to OnAfterRenderAsync, upgrade SqlClient to release version, update installer to Bootstrap 5.1.3 2021-12-01 08:22:59 -05:00
2f8a580854 Merge pull request #1840 from sbwalker/dev
Route parsing abstraction and optimization, site router performance improvements, migrate site-based concepts (favicon, PWA support) to server for performance and prerendering benefits, move ThemeBuilder interop logic to OnAfterRenderAsync, upgrade SqlClient to release version, update installer to Bootstrap 5.1.3
2021-12-01 08:14:46 -05:00
a21a2ab3bb Settings for ModuleDefinitions #1829
Add Update settings for the ModuleDefinition
2021-11-24 16:06:52 +01:00
03106526e9 Enhance the default site template with a Develop page that makes the Module Creator more discoverable for new users 2021-11-24 09:07:43 -05:00
a9ac3917b3 Merge pull request #1831 from sbwalker/dev
Enhance the default site template with a Develop page that makes the Module Creator more discoverable for new users
2021-11-24 08:59:54 -05:00
53ff491efd Assorted enhancements 2021-11-24 08:08:39 -05:00
1feb6ec452 Merge pull request #1830 from sbwalker/dev
Assorted enhancements
2021-11-24 08:00:49 -05:00
df00f53e54 Merge pull request #1823 from hishamco/tab-panel-localizer
Fix heading localization in TabPanel
2021-11-22 16:03:50 -05:00
be32af7588 Merge pull request #1827 from sbwalker/dev
refactored ErrorBoundary implementation to support logging
2021-11-22 16:03:17 -05:00
19be77ed49 refactored ErrorBoundary implementation to support logging 2021-11-22 16:11:44 -05:00
1c43c095bc Fix heading localization in TabPanel 2021-11-20 09:47:49 +03:00
59850f4869 Merge pull request #1815 from chlupac/ErrorBoundary
Implementing ErrorBoundary in ModuleInstance component
2021-11-18 16:14:35 -05:00
804b61ff95 Merge pull request #1820 from hishamco/swagger
Handle SchemaId in Swagger
2021-11-18 08:54:57 -05:00
d431c607ba Handle SchemaId in Swagger 2021-11-18 14:50:17 +03:00
b87b0489e9 Merge pull request #1812 from leigh-pointer/PageModules
Modification to Page Management component
2021-11-17 08:52:31 -05:00
2e593d44ee Merge pull request #1813 from leigh-pointer/ModuleDefinitionsInUse
Modification to Module Management
2021-11-17 08:49:00 -05:00
71354464e3 Merge pull request #1810 from leigh-pointer/ControlPanel
Page management buttons resizing
2021-11-17 08:44:39 -05:00
c48b4788c6 Merge pull request #1805 from chlupac/AliasFix
Fix - site with default alias (*) edit fail
2021-11-17 08:44:05 -05:00
fe9a7333ed Merge pull request #1798 from leigh-pointer/BreadCrumbs
Fix for #1797 Breadcrumbs render clickable
2021-11-17 08:43:55 -05:00
16d9e06db2 Merge pull request #1793 from 2sic-forks/dev
Add many PrivateApi attributes to hide unimportant stuff in docs
2021-11-17 08:43:35 -05:00
b40ee19735 ErrorBoundary 2021-11-17 11:22:24 +01:00
d5b0356625 Modification to Module Management
The component now reports back if the module is in use.  This will assist in housekeeping and removal of unused modules.
2021-11-16 00:37:57 +01:00
5ca77c3f64 Modification to Page Management component
Add a new tabpane that lists all the module on that page.  From here you are able to modify the module settings and or delete the module from a page.  Delete will send the module to the recycle bin.
2021-11-15 23:26:20 +01:00
54e9307795 Page management buttons resizing
When the language is changed, in this instance Dutch the buttons are not resized to fit the caption.  This small fix rectifies this.
2021-11-15 21:14:40 +01:00
60d7e45048 Fix - site with default alias (*) edit fail 2021-11-14 10:22:01 +01:00
931559cca8 Update README.md 2021-11-12 09:42:34 -05:00
2567c2937d Fix for #1797 Breadcrumbs render clickable
This fixes the issue when the page property IsClickable is set to false the breadcrum for the page is not clickable.
2021-11-12 07:07:15 +01:00
5b8e6d4df6 Add many PrivateApi attributes to hide unimportant stuff in docs 2021-11-11 20:01:55 +01:00
087c053bd5 Merge pull request #1791 from oqtane/master
Merge pull request #1790 from oqtane/dev
2021-11-11 10:13:49 -05:00
7e699136d7 Merge pull request #1790 from oqtane/dev
version 3.0.0 release
2021-11-11 10:13:00 -05:00
b7bbfe2a46 Merge pull request #1789 from sbwalker/dev
updated database provider references
2021-11-11 09:12:49 -05:00
69783b0709 updated database provider references 2021-11-11 09:21:01 -05:00
614041d55e update Blazor theme with bootstrap bundle js 2021-11-11 07:50:24 -05:00
856f61c2ee Merge pull request #1787 from sbwalker/dev
update Blazor theme with bootstrap bundle js
2021-11-11 07:42:10 -05:00
b0b196a522 Merge pull request #1786 from leigh-pointer/ActionMenu
Fix for Action Menus not displaying. #1785
2021-11-11 07:38:25 -05:00
62f04d239f Fix for Action Menus not displaying. #1785
Dropdowns are built on a third party library, Popper, which provides dynamic positioning and viewport detection.  / bootstrap.bundle.js which contains Popper. Popper isn’t used to position dropdowns in navbars though as dynamic positioning isn’t required.

updated the Bootstrap to reference the ../5.1.3/js/bootstrap.bundle.min.js
2021-11-11 10:27:09 +01:00
4210d10fca Merge pull request #1782 from leigh-pointer/Bootstrap
Upgrade to 5.1.3 Bootstrap and Bootswatch Cyborg
2021-11-10 17:25:27 -05:00
d02842f0ea Merge branch 'dev' into Bootstrap 2021-11-10 17:25:16 -05:00
2543b7db79 Upgrade to 5.1.3 Bootstrap and Bootswatch Cyborg
Fixed issue with OffCanvas not rendering properly.
2021-11-10 22:34:19 +01:00
41f430429b Merge pull request #1781 from sbwalker/dev
fix UX in module/theme creators
2021-11-10 15:48:36 -05:00
4ed4f8d942 fix UX in module/theme creators 2021-11-10 15:56:51 -05:00
cc5396801b update Test project dependencies 2021-11-10 13:12:38 -05:00
a72dc36d67 Merge pull request #1780 from sbwalker/dev
update Test project dependencies
2021-11-10 13:04:23 -05:00
50989e4e1e Merge pull request #1778 from sbwalker/dev
use Cloudflare CDN for static resources
2021-11-10 08:52:38 -05:00
41487440e3 use Cloudflare CDN for static resources 2021-11-10 09:00:48 -05:00
af72750354 Merge pull request #1776 from leigh-pointer/ReplaceRoot
[RootFolder] was missing from Release.cmd
2021-11-10 08:44:15 -05:00
a58dc49acc [RootFolder] was missing from Release.cmd
One of the replace tokens was not added to the second command line.
2021-11-10 14:26:26 +01:00
6dbb493d10 updating module and theme templates 2021-11-08 15:12:41 -05:00
b8c37ff5d7 Merge pull request #1772 from sbwalker/dev
updating module and theme templates
2021-11-08 15:04:29 -05:00
04319195c6 update to official .NET 6 release 2021-11-08 14:55:24 -05:00
aadd78b7ac Merge pull request #1771 from sbwalker/dev
update to official .NET 6 release
2021-11-08 14:47:12 -05:00
e23da1f5fb Update README.md 2021-11-06 07:53:54 -04:00
50eeaf8497 add support for TrustServerCertificate connection string option for SQL Server 2021-11-05 16:01:00 -04:00
039202559f Merge pull request #1764 from sbwalker/dev
add support for TrustServerCertificate connection string option for SQL Server
2021-11-05 15:53:00 -04:00
5419032e8d upgrade module and theme templates to .NET6 2021-11-05 12:53:13 -04:00
017a92c4bc Merge pull request #1763 from sbwalker/dev
upgrade module and theme templates to .NET6
2021-11-05 12:45:10 -04:00
a16040a595 remove unnecessary cascading parameter to improve efficiency 2021-11-05 09:03:12 -04:00
3f6936a999 Merge pull request #1762 from sbwalker/dev
remove unnecessary cascading parameter to improve efficiency
2021-11-05 08:55:03 -04:00
d3f3359f66 fix #1745 - error on WebAssembly when logging out 2021-11-04 08:06:28 -04:00
3f110aaabd Merge pull request #1761 from sbwalker/dev
fix #1745 - error on WebAssembly when logging out
2021-11-04 07:58:22 -04:00
4e884d57ca Merge pull request #1760 from leigh-pointer/Navigation
Navigation
2021-11-04 07:34:10 -04:00
efbe0562f9 Navigation was not completed 2021-11-04 06:09:19 +01:00
096dfea1a6 Merge remote-tracking branch 'oqtane/dev' into dev 2021-11-04 05:53:42 +01:00
bd5a827593 fix #1746 - SQL Server installation needs to allow configuration of encryption setting on .NET 6 2021-11-03 16:37:37 -04:00
404bcaddd4 Merge pull request #1759 from sbwalker/dev
fix #1746 - SQL Server installation needs to allow configuration of encryption setting on .NET 6
2021-11-03 16:29:45 -04:00
d2d52a7eb3 update SqlClient to latest preview version 2021-11-03 14:42:24 -04:00
1761c47713 Merge pull request #1758 from sbwalker/dev
update SqlClient to latest preview version
2021-11-03 14:34:28 -04:00
82e97aa4fa Merge pull request #1750 from leigh-pointer/BlazorTheme
Fix for #1736 Blazor theme not rendering correctly
2021-11-03 12:32:45 -04:00
e598178869 Merge pull request #1752 from leigh-pointer/PView
Fix for #1749 navigate to sub sub pages
2021-11-03 12:32:34 -04:00
b6f89195ab Merge pull request #1754 from leigh-pointer/1753
Update for #1753 Date format for the Audit
2021-11-03 12:31:43 -04:00
7aa92c039a Merge remote-tracking branch 'oqtane/dev' into dev 2021-11-02 20:06:37 +01:00
fff36949b7 Fix for #1749 navigate to sub sub pages
Also added missing "Browse" localization from site/index,resx
2021-11-02 19:59:59 +01:00
c524f17978 Merge pull request #1757 from sbwalker/dev
Fix #1751 - error when creating site with new tenant
2021-11-02 14:41:21 -04:00
e0a0497dd2 Fix #1751 - error when creating site with new tenant 2021-11-02 14:49:06 -04:00
fce9220dcb Update for #1753 Date format for the Audit
Added Parameter DateTimeFormat with default value of  "MMM dd yyyy HH:mm:ss"
2021-11-02 07:01:24 +01:00
a8ddb64b49 Fix for #1749 navigate to sub sub pages
Added Open button that will navigate to sub pages
2021-11-02 05:57:05 +01:00
6d8df2661c modification for responsive theme
small modification to ensure theme is responsive
2021-10-31 07:08:19 +01:00
1659de3a2b Fix for #1736 Server Css
Update to the Server file Theme.css
2021-10-28 20:35:48 +02:00
9752c72998 Fix for #1736 Blazor theme not rendering correctly
Fix to the Default theme and container
2021-10-28 19:43:51 +02:00
db2e3a518d Update README.md 2021-10-27 10:04:00 -04:00
94b20662a4 Update README.md 2021-10-26 08:37:09 -04:00
7bfc0998fd fix #1713 - link to home path displays login page 2021-10-26 08:30:50 -04:00
6707f0efdf Update README.md 2021-10-26 08:27:18 -04:00
c7fe5a538f Merge pull request #1735 from sbwalker/dev
fix #1713 - link to home path displays login page
2021-10-26 08:23:12 -04:00
9b20006938 removed hidden form fields which are unnecessary as a result of recent changes 2021-10-22 10:33:09 -04:00
0b258bd384 Merge pull request #1730 from sbwalker/dev
removed hidden form fields which are unnecessary as a result of recent changes
2021-10-22 10:25:41 -04:00
2302c17273 Update README.md 2021-10-19 16:21:13 -04:00
b619699637 Update README.md 2021-10-19 16:19:43 -04:00
34434e03fe Merge pull request #1725 from sbwalker/dev
upgrade to .NET 6 and increment version to 3.0.0
2021-10-19 15:27:19 -04:00
29bd31f609 upgrade to .NET 6 and increment version to 3.0.0 2021-10-19 15:33:03 -04:00
cf69f9e4c4 Add proper help text to aliases field in default resource file for Site Settings. Set default value for new ShowLogin parameter in Login theme component. 2021-10-17 13:27:12 -04:00
028c9bf0a8 Merge pull request #1720 from sbwalker/dev
Add proper help text to aliases field in default resource file for Site Settings. Set default value for new ShowLogin parameter in Login theme component.
2021-10-17 13:20:24 -04:00
3e9a4f2c1a Fixed validation issue in Role Managment - Users. Modified FileManager component to allow Folder parameter to contain a folder path which is translated to a FolderId internally and refactored Packages folder logic. 2021-10-06 17:20:44 -04:00
960543d14d Merge pull request #1709 from sbwalker/dev
Fixed validation issue in Role Managment - Users. Modified FileManager component to allow Folder parameter to contain a folder path which is translated to a FolderId internally and refactored Packages folder logic.
2021-10-06 17:14:45 -04:00
299674f53b Update README.md 2021-10-06 09:55:47 -04:00
306b78b526 Added ability for Runtime and RenderMode to be set per Site - enabling the framework to support multiple hosting models concurrently in the same installation. Fixed WebAssembly Prerendering issue (this also resolved the issue where the component taghelper was not passing parameters correctly to the app when running on WebAssembly). Fix #1702 - remove web,config from upgrade package. 2021-10-05 14:32:05 -04:00
f369382a54 Merge pull request #1708 from sbwalker/dev
Added ability for Runtime and RenderMode to be set per Site - enabling the framework to support multiple hosting models concurrently in the same installation. Fixed WebAssembly Prerendering issue (this also resolved the issue where the component taghelper was not passing parameters correctly to the app when running on WebAssembly). Fix #1702 - remove web,config from upgrade package.
2021-10-05 14:25:12 -04:00
ac67d88e74 fix logic which sometimes results in System.InvalidOperationException: The value of IsFixed cannot be changed dynamically 2021-10-01 15:58:17 -04:00
1f4f70009c Merge pull request #1704 from sbwalker/dev
fix logic which sometimes results in  System.InvalidOperationException: The value of IsFixed cannot be changed dynamically
2021-10-01 15:51:26 -04:00
838d918451 Merge pull request #1701 from leigh-pointer/1690-1
1690 User Management Tab needs clicking to render UI when language is not default.
2021-10-01 11:23:01 -04:00
8e6c73d2bc Merge pull request #1703 from sbwalker/dev
Allow root page paths (rather than specifying a magic "home" string). More UX improvements to FileManager and Pager.
2021-10-01 11:22:02 -04:00
aeb599867c Allow root page paths (rather than specifying a magic "home" string). More UX improvements to FileManager and Pager. 2021-10-01 11:28:48 -04:00
2fe93d4e64 Fix for #1690 Tab needs clicking to render UI
User Management Tab needs clicking to render UI when language is not default.  Modification to the TabPanel fixes the issue without  forcing the Heading property to be populated.
2021-09-29 18:05:59 +02:00
3e789e0642 UX improvements to FileManager and Pager components 2021-09-29 10:46:23 -04:00
a762f206a1 Merge pull request #1699 from sbwalker/dev
UX improvements to FileManager and Pager components
2021-09-29 10:39:43 -04:00
c0b13a1f09 2.3.1 database provider packages 2021-09-27 14:22:23 -04:00
070ddff1b4 Merge pull request #1697 from sbwalker/dev
2.3.1 database provider packages
2021-09-27 14:15:37 -04:00
9d0770e360 Merge pull request #1696 from oqtane/master
Merge pull request #1695 from oqtane/dev
2021-09-27 14:04:23 -04:00
088cb2a30e Merge pull request #1695 from oqtane/dev
2.3.1 release
2021-09-27 14:03:55 -04:00
c2be84a367 increment version to 2.3.1 2021-09-27 11:43:57 -04:00
4f61dd7bb3 Merge pull request #1694 from sbwalker/dev
increment version to 2.3.1
2021-09-27 11:37:34 -04:00
30fb6fd8e2 Merge pull request #1693 from sbwalker/dev
fix #1691 - AntiForgeryToken header not being set during startup
2021-09-27 08:37:27 -04:00
4bfb5d9f34 fix #1691 - AntiForgeryToken header not being set during startup 2021-09-27 08:44:16 -04:00
023f29491d Update README.md 2021-09-25 09:08:20 -04:00
5166fe2b41 Update README.md 2021-09-25 09:04:36 -04:00
71aa41d55e Update README.md 2021-09-25 09:03:44 -04:00
f6fd50f449 Update README.md 2021-09-25 09:02:06 -04:00
81e2f7c288 Update README.md 2021-09-25 09:01:40 -04:00
217 changed files with 7665 additions and 3758 deletions

View File

@ -15,7 +15,7 @@
<div style="@_display"> <div style="@_display">
<CascadingAuthenticationState> <CascadingAuthenticationState>
<CascadingValue Value="@PageState"> <CascadingValue Value="@PageState">
<SiteRouter Runtime="@Runtime" RenderMode="@RenderMode" OnStateChange="@ChangeState" /> <SiteRouter Runtime="@Runtime" RenderMode="@RenderMode" VisitorId="@VisitorId" OnStateChange="@ChangeState" />
</CascadingValue> </CascadingValue>
</CascadingAuthenticationState> </CascadingAuthenticationState>
</div> </div>
@ -39,6 +39,9 @@
[Parameter] [Parameter]
public string RenderMode { get; set; } public string RenderMode { get; set; }
[Parameter]
public int VisitorId { get; set; }
private bool _initialized = false; private bool _initialized = false;
private string _display = "display: none;"; private string _display = "display: none;";
private Installation _installation = new Installation { Success = false, Message = "" }; private Installation _installation = new Installation { Success = false, Message = "" };
@ -47,11 +50,13 @@
protected override async Task OnParametersSetAsync() protected override async Task OnParametersSetAsync()
{ {
SiteState.AntiForgeryToken = AntiForgeryToken;
InstallationService.SetAntiForgeryTokenHeader(AntiForgeryToken);
_installation = await InstallationService.IsInstalled(); _installation = await InstallationService.IsInstalled();
if (_installation.Alias != null) if (_installation.Alias != null)
{ {
SiteState.Alias = _installation.Alias; SiteState.Alias = _installation.Alias;
SiteState.AntiForgeryToken = AntiForgeryToken;
} }
else else
{ {
@ -60,19 +65,10 @@
_initialized = true; _initialized = true;
} }
protected override async Task OnAfterRenderAsync(bool firstRender) protected override void OnAfterRender(bool firstRender)
{ {
if (firstRender) if (firstRender)
{ {
if (string.IsNullOrEmpty(AntiForgeryToken))
{
// parameter values are not set when running on WebAssembly (seems to be a .NET 5 bug) - need to retrieve using JSInterop
var interop = new Interop(JSRuntime);
AntiForgeryToken = await interop.GetElementByName(Constants.RequestVerificationToken);
SiteState.AntiForgeryToken = AntiForgeryToken;
Runtime = await interop.GetElementByName("app_runtime");
RenderMode = await interop.GetElementByName("app_rendermode");
}
_display = ""; _display = "";
StateHasChanged(); StateHasChanged();
} }

View File

@ -46,6 +46,8 @@ namespace Microsoft.Extensions.DependencyInjection
services.AddScoped<ILocalizationService, LocalizationService>(); services.AddScoped<ILocalizationService, LocalizationService>();
services.AddScoped<ILanguageService, LanguageService>(); services.AddScoped<ILanguageService, LanguageService>();
services.AddScoped<IDatabaseService, DatabaseService>(); services.AddScoped<IDatabaseService, DatabaseService>();
services.AddScoped<IUrlMappingService, UrlMappingService>();
services.AddScoped<IVisitorService, VisitorService>();
services.AddScoped<ISyncService, SyncService>(); services.AddScoped<ISyncService, SyncService>();
return services; return services;

View File

@ -24,7 +24,7 @@
if (!String.IsNullOrEmpty(_server) && !String.IsNullOrEmpty(_database)) if (!String.IsNullOrEmpty(_server) && !String.IsNullOrEmpty(_database))
{ {
connectionString = $"Data Source={_server};AttachDbFilename=|DataDirectory|\\{_database}.mdf;Initial Catalog={_database};Integrated Security=SSPI;"; connectionString = $"Data Source={_server};AttachDbFilename=|DataDirectory|\\{_database}.mdf;Initial Catalog={_database};Integrated Security=SSPI;Encrypt=false;";
} }
return connectionString; return connectionString;

View File

@ -1,6 +1,7 @@
@namespace Oqtane.Installer.Controls @namespace Oqtane.Installer.Controls
@implements Oqtane.Interfaces.IDatabaseConfigControl @implements Oqtane.Interfaces.IDatabaseConfigControl
@inject IStringLocalizer<SqlServerConfig> Localizer @inject IStringLocalizer<SqlServerConfig> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="server" HelpText="Enter the database server" ResourceKey="Server">Server:</Label> <Label Class="col-sm-3" For="server" HelpText="Enter the database server" ResourceKey="Server">Server:</Label>
@ -38,6 +39,27 @@
</div> </div>
</div> </div>
} }
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="encryption" HelpText="Specify if you are using an encrypted database connection. It is highly recommended to use encryption in a production environment." ResourceKey="Encryption">Encryption:</Label>
<div class="col-sm-9">
<select id="encryption" class="form-select custom-select" @bind="@_encryption">
<option value="true">@SharedLocalizer["True"]</option>
<option value="false">@SharedLocalizer["False"]</option>
</select>
</div>
</div>
@if (_encryption == "true")
{
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="trustservercertificate" HelpText="Specify the type of certificate you are using for encryption" ResourceKey="TrustServerCertificate">Certificate:</Label>
<div class="col-sm-9">
<select id="encryption" class="form-select custom-select" @bind="@_trustservercertificate">
<option value="true">@Localizer["Self Signed"]</option>
<option value="false">@Localizer["Verifiable"]</option>
</select>
</div>
</div>
}
@code { @code {
private string _server = String.Empty; private string _server = String.Empty;
@ -45,6 +67,8 @@
private string _security = "integrated"; private string _security = "integrated";
private string _uid = String.Empty; private string _uid = String.Empty;
private string _pwd = String.Empty; private string _pwd = String.Empty;
private string _encryption = "false";
private string _trustservercertificate = "false";
public string GetConnectionString() public string GetConnectionString()
{ {
@ -61,15 +85,10 @@
} }
else else
{ {
if (!String.IsNullOrEmpty(_uid) && !String.IsNullOrEmpty(_pwd)) connectionString += $"User ID={_uid};Password={_pwd};";
{
connectionString += $"User ID={_uid};Password={_pwd};";
}
else
{
connectionString = String.Empty;
}
} }
connectionString += $"Encrypt={_encryption};";
connectionString += $"TrustServerCertificate={_trustservercertificate};";
return connectionString; return connectionString;
} }

View File

@ -24,10 +24,10 @@
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="databasetype" HelpText="Select the type of database you wish to use" ResourceKey="DatabaseType">Database:</Label> <Label Class="col-sm-3" For="databasetype" HelpText="Select the type of database you wish to use" ResourceKey="DatabaseType">Database:</Label>
<div class="col-sm-9"> <div class="col-sm-9">
<select id="databasetype" class="form-select custom-select" value="@_databaseName" @onchange="(e => DatabaseChanged(e))"> @if (_databases != null)
@if (_databases != null) {
{ <select id="databasetype" class="form-select custom-select" value="@_databaseName" @onchange="(e => DatabaseChanged(e))">
foreach (var database in _databases) @foreach (var database in _databases)
{ {
if (database.IsDefault) if (database.IsDefault)
{ {
@ -38,8 +38,8 @@
<option value="@database.Name">@Localizer[@database.Name]</option> <option value="@database.Name">@Localizer[@database.Name]</option>
} }
} }
} </select>
</select> }
</div> </div>
</div> </div>
@{ @{
@ -96,23 +96,31 @@
</div> </div>
@code { @code {
private List<Database> _databases; private List<Database> _databases;
private string _databaseName = "LocalDB"; private string _databaseName;
private Type _databaseConfigType; private Type _databaseConfigType;
private object _databaseConfig; private object _databaseConfig;
private RenderFragment DatabaseConfigComponent { get; set; } private RenderFragment DatabaseConfigComponent { get; set; }
private string _hostUsername = string.Empty; private string _hostUsername = string.Empty;
private string _hostPassword = string.Empty; private string _hostPassword = string.Empty;
private string _confirmPassword = string.Empty; private string _confirmPassword = string.Empty;
private string _hostEmail = string.Empty; private string _hostEmail = string.Empty;
private bool _register = true; private bool _register = true;
private string _message = string.Empty; private string _message = string.Empty;
private string _loadingDisplay = "display: none;"; private string _loadingDisplay = "display: none;";
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
_databases = await DatabaseService.GetDatabasesAsync(); _databases = await DatabaseService.GetDatabasesAsync();
if (_databases.Exists(item => item.IsDefault))
{
_databaseName = _databases.Find(item => item.IsDefault).Name;
}
else
{
_databaseName = "LocalDB";
}
LoadDatabaseConfigComponent(); LoadDatabaseConfigComponent();
} }
@ -150,8 +158,8 @@
if (firstRender) if (firstRender)
{ {
var interop = new Interop(JSRuntime); var interop = new Interop(JSRuntime);
await interop.IncludeLink("", "stylesheet", "https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css", "text/css", "sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC", "anonymous", ""); await interop.IncludeLink("", "stylesheet", "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.1.3/css/bootstrap.min.css", "text/css", "sha512-GQGU0fMMi238uA+a/bdWJfpUGKUkBdgfFdgBm72SUQ6BeyWjoY/ton0tEjH+OSH9iP4Dfh+7HM0I9f5eR0L/4w==", "anonymous", "");
await interop.IncludeScript("", "https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js", "sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM", "anonymous", "", "head", ""); await interop.IncludeScript("", "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.1.3/js/bootstrap.bundle.min.js", "sha512-pax4MlgXjHEPfCwcJLQhigY7+N8rt6bVvWLFyUMuxShv170X53TRzGPmPkZmGBhk+jikR8WBM4yl7A9WMHHqvg==", "anonymous", "", "head", "");
} }
} }

View File

@ -13,7 +13,7 @@
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="upload" HelpText="Upload the file you want" ResourceKey="Upload">Upload: </Label> <Label Class="col-sm-3" For="upload" HelpText="Upload the file you want" ResourceKey="Upload">Upload: </Label>
<div class="col-sm-9"> <div class="col-sm-9">
<FileManager UploadMultiple="true" ShowFiles="false" FolderId="@_folderId" /> <FileManager UploadMultiple="true" ShowFiles="false" FolderId="@_folderId" ShowSuccess="true" />
</div> </div>
</div> </div>
</div> </div>

View File

@ -113,9 +113,9 @@ else
<TabPanel Name="Upload" ResourceKey="Upload" Security="SecurityAccessLevel.Host"> <TabPanel Name="Upload" ResourceKey="Upload" Security="SecurityAccessLevel.Host">
<div class="container"> <div class="container">
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" HelpText="Upload one or more translations. Once they are uploaded click Install to complete the installation." ResourceKey="Module">Language: </Label> <Label Class="col-sm-3" HelpText="Upload one or more translations. Once they are uploaded click Install to complete the installation." ResourceKey="LanguageUpload">Language: </Label>
<div class="col-sm-9"> <div class="col-sm-9">
<FileManager Filter="nupkg" ShowFiles="true" Folder="Packages" UploadMultiple="true" /> <FileManager Folder="@Constants.PackagesFolder" UploadMultiple="true" />
</div> </div>
</div> </div>
</div> </div>
@ -317,7 +317,7 @@ else
{ {
try try
{ {
await PackageService.DownloadPackageAsync(_packageid, _version, "Packages"); await PackageService.DownloadPackageAsync(_packageid, _version, Constants.PackagesFolder);
await logger.LogInformation("Language Package {Name} {Version} Downloaded Successfully", _packageid, _version); await logger.LogInformation("Language Package {Name} {Version} Downloaded Successfully", _packageid, _version);
AddModuleMessage(Localizer["Success.Language.Download"], MessageType.Success); AddModuleMessage(Localizer["Success.Language.Download"], MessageType.Success);
StateHasChanged(); StateHasChanged();
@ -350,7 +350,7 @@ else
var localizationCookieValue = CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture)); var localizationCookieValue = CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture));
await interop.SetCookie(CookieRequestCultureProvider.DefaultCookieName, localizationCookieValue, 360); await interop.SetCookie(CookieRequestCultureProvider.DefaultCookieName, localizationCookieValue, 360);
NavigationManager.NavigateTo(NavigationManager.Uri, forceLoad: true); NavigationManager.NavigateTo(NavigationManager.Uri, true);
} }
} }
} }

View File

@ -97,7 +97,7 @@ else
{ {
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host)) if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
{ {
await PackageService.DownloadPackageAsync(Constants.PackageId + ".Client." + code, Constants.Version, "Packages"); await PackageService.DownloadPackageAsync(Constants.PackageId + ".Client." + code, Constants.Version, Constants.PackagesFolder);
await logger.LogInformation("Translation Downloaded {Code} {Version}", code, Constants.Version); await logger.LogInformation("Translation Downloaded {Code} {Version}", code, Constants.Version);
await PackageService.InstallPackagesAsync(); await PackageService.InstallPackagesAsync();
AddModuleMessage(string.Format(Localizer["Success.Language.Install"], NavigateUrl("admin/system")), MessageType.Success); AddModuleMessage(string.Format(Localizer["Success.Language.Install"], NavigateUrl("admin/system")), MessageType.Success);

View File

@ -16,7 +16,7 @@
<text>...</text> <text>...</text>
</Authorizing> </Authorizing>
<Authorized> <Authorized>
<ModuleMessage Message="@Localizer["Info.SignedIn"]" Type="MessageType.Info" /> <div>@Localizer["Info.SignedIn"]</div>
</Authorized> </Authorized>
<NotAuthorized> <NotAuthorized>
<form @ref="login" class="@(validated ? "was-validated" : "needs-validation")" novalidate> <form @ref="login" class="@(validated ? "was-validated" : "needs-validation")" novalidate>

View File

@ -10,100 +10,32 @@
@inject IStringLocalizer<SharedResources> SharedLocalizer @inject IStringLocalizer<SharedResources> SharedLocalizer
<div class="container"> <div class="container">
<div class="row mb-1 align-items-center">
<div class="col-sm-9">
</div>
</div>
<div class="row mb-1 align-items-center">
<div class="col-sm-9">
</div>
</div>
<div class="row mb-1 align-items-center">
<div class="col-sm-9">
</div>
</div>
<div class="row mb-1 align-items-center">
<div class="col-sm-9">
</div>
</div>
<div class="row mb-1 align-items-center">
<div class="col-sm-9">
</div>
</div>
<div class="row mb-1 align-items-center">
<div class="col-sm-9">
</div>
</div>
<div class="row mb-1 align-items-center">
<div class="col-sm-9">
</div>
</div>
<div class="row mb-1 align-items-center">
<div class="col-sm-9">
</div>
</div>
<div class="row mb-1 align-items-center">
<div class="col-sm-9">
</div>
</div>
<div class="row mb-1 align-items-center">
<div class="col-sm-9">
</div>
</div>
<div class="row mb-1 align-items-center">
<div class="col-sm-9">
</div>
</div>
</div>
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="dateTime" HelpText="The date and time of this log" ResourceKey="DateTime">Date/Time: </Label> <Label Class="col-sm-3" For="dateTime" HelpText="The date and time of this log" ResourceKey="DateTime">Date/Time: </Label>
<div class="col-sm-9"> <div class="col-sm-9">
<input id="dateTime" class="form-control" @bind="@_logDate" readonly /> <input id="dateTime" class="form-control" @bind="@_logDate" readonly />
</div> </div>
</div> </div>
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="level" HelpText="The level of this log" ResourceKey="Level">Level: </Label> <Label Class="col-sm-3" For="level" HelpText="The level of this log" ResourceKey="Level">Level: </Label>
<div class="col-sm-9"> <div class="col-sm-9">
<input id="level" class="form-control" @bind="@_level" readonly /> <input id="level" class="form-control" @bind="@_level" readonly />
</div> </div>
</div> </div>
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="feature" HelpText="The feature that was affected" ResourceKey="Feature">Feature: </Label> <Label Class="col-sm-3" For="feature" HelpText="The feature that was affected" ResourceKey="Feature">Feature: </Label>
<div class="col-sm-9"> <div class="col-sm-9">
<input id="feature" class="form-control" @bind="@_feature" readonly /> <input id="feature" class="form-control" @bind="@_feature" readonly />
</div> </div>
</div> </div>
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="function" HelpText="The function that was performed" ResourceKey="Function">Function: </Label> <Label Class="col-sm-3" For="function" HelpText="The function that was performed" ResourceKey="Function">Function: </Label>
<div class="col-sm-9"> <div class="col-sm-9">
<input id="function" class="form-control" @bind="@_function" readonly /> <input id="function" class="form-control" @bind="@_function" readonly />
</div> </div>
</div> </div>
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="category" HelpText="The categories that were affected" ResourceKey="Category">Category: </Label> <Label Class="col-sm-3" For="category" HelpText="The categories that were affected" ResourceKey="Category">Category: </Label>
<div class="col-sm-9"> <div class="col-sm-9">
<input id="category" class="form-control" @bind="@_category" readonly /> <input id="category" class="form-control" @bind="@_category" readonly />
</div> </div>
@ -111,7 +43,7 @@
@if (_pageName != string.Empty) @if (_pageName != string.Empty)
{ {
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="page" HelpText="The page that was affected" ResourceKey="Page">Page: </Label> <Label Class="col-sm-3" For="page" HelpText="The page that was affected" ResourceKey="Page">Page: </Label>
<div class="col-sm-9"> <div class="col-sm-9">
<input id="page" class="form-control" @bind="@_pageName" readonly /> <input id="page" class="form-control" @bind="@_pageName" readonly />
</div> </div>
@ -120,7 +52,7 @@
@if (_moduleTitle != string.Empty) @if (_moduleTitle != string.Empty)
{ {
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="module" HelpText="The module that was affected" ResourceKey="Module">Module: </Label> <Label Class="col-sm-3" For="module" HelpText="The module that was affected" ResourceKey="Module">Module: </Label>
<div class="col-sm-9"> <div class="col-sm-9">
<input id="module" class="form-control" @bind="@_moduleTitle" readonly /> <input id="module" class="form-control" @bind="@_moduleTitle" readonly />
</div> </div>
@ -129,26 +61,26 @@
@if (_username != string.Empty) @if (_username != string.Empty)
{ {
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="user" HelpText="The user that caused this log" ResourceKey="User">User: </Label> <Label Class="col-sm-3" For="user" HelpText="The user that caused this log" ResourceKey="User">User: </Label>
<div class="col-sm-9"> <div class="col-sm-9">
<input id="user" class="form-control" @bind="@_username" readonly /> <input id="user" class="form-control" @bind="@_username" readonly />
</div> </div>
</div> </div>
} }
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="url" HelpText="The url the log comes from" ResourceKey="Url">Url: </Label> <Label Class="col-sm-3" For="url" HelpText="The url the log comes from" ResourceKey="Url">Url: </Label>
<div class="col-sm-9"> <div class="col-sm-9">
<input id="url" class="form-control" @bind="@_url" readonly /> <input id="url" class="form-control" @bind="@_url" readonly />
</div> </div>
</div> </div>
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="template" HelpText="What the log is about" ResourceKey="Template">Template: </Label> <Label Class="col-sm-3" For="template" HelpText="What the log is about" ResourceKey="Template">Template: </Label>
<div class="col-sm-9"> <div class="col-sm-9">
<input id="template" class="form-control" @bind="@_template" readonly /> <input id="template" class="form-control" @bind="@_template" readonly />
</div> </div>
</div> </div>
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="message" HelpText="The message that the system generated" ResourceKey="Message">Message: </Label> <Label Class="col-sm-3" For="message" HelpText="The message that the system generated" ResourceKey="Message">Message: </Label>
<div class="col-sm-9"> <div class="col-sm-9">
<textarea id="message" class="form-control" @bind="@_message" rows="5" readonly></textarea> <textarea id="message" class="form-control" @bind="@_message" rows="5" readonly></textarea>
</div> </div>
@ -156,24 +88,25 @@
@if (!string.IsNullOrEmpty(_exception)) @if (!string.IsNullOrEmpty(_exception))
{ {
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="exception" HelpText="The exceptions generated by the system" ResourceKey="Exception">Exception: </Label> <Label Class="col-sm-3" For="exception" HelpText="The exceptions generated by the system" ResourceKey="Exception">Exception: </Label>
<div class="col-sm-9"> <div class="col-sm-9">
<textarea id="exception" class="form-control" @bind="@_exception" rows="5" readonly></textarea> <textarea id="exception" class="form-control" @bind="@_exception" rows="5" readonly></textarea>
</div> </div>
</div> </div>
} }
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="properties" HelpText="The properties that were affected" ResourceKey="Properties">Properties: </Label> <Label Class="col-sm-3" For="properties" HelpText="The properties that were affected" ResourceKey="Properties">Properties: </Label>
<div class="col-sm-9"> <div class="col-sm-9">
<textarea id="properties" class="form-control" @bind="@_properties" rows="5" readonly></textarea> <textarea id="properties" class="form-control" @bind="@_properties" rows="5" readonly></textarea>
</div> </div>
</div> </div>
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="server" HelpText="The server that was affected" ResourceKey="Server">Server: </Label> <Label Class="col-sm-3" For="server" HelpText="The server that was affected" ResourceKey="Server">Server: </Label>
<div class="col-sm-9"> <div class="col-sm-9">
<input id="server" class="form-control" @bind="@_server" readonly /> <input id="server" class="form-control" @bind="@_server" readonly />
</div> </div>
</div> </div>
</div>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink> <NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>

View File

@ -51,11 +51,11 @@ else
{ {
<Pager Items="@_logs"> <Pager Items="@_logs">
<Header> <Header>
<th style="width: 1px;">&nbsp;</th> <th style="width: 1px;">&nbsp;</th>
<th>@Localizer["Date"]</th> <th>@Localizer["Date"]</th>
<th>@Localizer["Level"]</th> <th>@Localizer["Level"]</th>
<th>@Localizer["Feature"]</th> <th>@Localizer["Feature"]</th>
<th>@Localizer["Function"]</th> <th>@Localizer["Function"]</th>
</Header> </Header>
<Row> <Row>
<td class="@GetClass(context.Function)"><ActionLink Action="Detail" Parameters="@($"id=" + context.LogId.ToString())" ResourceKey="LogDetails" /></td> <td class="@GetClass(context.Function)"><ActionLink Action="Detail" Parameters="@($"id=" + context.LogId.ToString())" ResourceKey="LogDetails" /></td>

View File

@ -10,70 +10,69 @@
@if (string.IsNullOrEmpty(_moduledefinitionname) && _templates != null) @if (string.IsNullOrEmpty(_moduledefinitionname) && _templates != null)
{ {
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate> <form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
<div class="container"> <div class="container">
<div class="row mb-1 align-items-center"> <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> <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"> <div class="col-sm-9">
<input id="owner" class="form-control" @bind="@_owner" required /> <input id="owner" class="form-control" @bind="@_owner" required />
</div> </div>
</div> </div>
<div class="row mb-1 align-items-center"> <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> <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"> <div class="col-sm-9">
<input id="module" class="form-control" @bind="@_module" required /> <input id="module" class="form-control" @bind="@_module" required />
</div> </div>
</div> </div>
<div class="row mb-1 align-items-center"> <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> <Label Class="col-sm-3" For="description" HelpText="Enter a short description for the module" ResourceKey="Description">Description: </Label>
<div class="col-sm-9"> <div class="col-sm-9">
<textarea id="description" class="form-control" @bind="@_description" rows="3" ></textarea> <textarea id="description" class="form-control" @bind="@_description" rows="3" ></textarea>
</div> </div>
</div> </div>
<div class="row mb-1 align-items-center"> <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> <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"> <div class="col-sm-9">
<select id="template" class="form-select" @onchange="(e => TemplateChanged(e))" required> <select id="template" class="form-select" @onchange="(e => TemplateChanged(e))" required>
<option value="-">&lt;@Localizer["Template.Select"]&gt;</option> <option value="-">&lt;@Localizer["Template.Select"]&gt;</option>
@foreach (Template template in _templates) @foreach (Template template in _templates)
{ {
<option value="@template.Name">@template.Title</option> <option value="@template.Name">@template.Title</option>
} }
</select> </select>
</div> </div>
</div> </div>
<div class="row mb-1 align-items-center"> <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> <Label Class="col-sm-3" For="reference" HelpText="Select a framework reference version" ResourceKey="FrameworkReference">Framework Reference: </Label>
<div class="col-sm-9"> <div class="col-sm-9">
<select id="reference" class="form-select" @bind="@_reference" required> <select id="reference" class="form-select" @bind="@_reference" required>
@foreach (string version in _versions) @foreach (string version in _versions)
{ {
if (Version.Parse(version).CompareTo(Version.Parse(_minversion)) >= 0) if (Version.Parse(version).CompareTo(Version.Parse(_minversion)) >= 0)
{ {
<option value="@(version)">@(version)</option> <option value="@(version)">@(version)</option>
} }
} }
<option value="local">@SharedLocalizer["LocalVersion"]</option> <option value="local">@SharedLocalizer["LocalVersion"]</option>
</select> </select>
</div> </div>
</div> </div>
@if (!string.IsNullOrEmpty(_location)) @if (!string.IsNullOrEmpty(_location))
{ {
<div class="row mb-1 align-items-center"> <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> <Label Class="col-sm-3" For="location" HelpText="Location where the module will be created" ResourceKey="Location">Location: </Label>
<div class="col-sm-9"> <div class="col-sm-9">
<input id="module" class="form-control" @bind="@_location" readonly /> <input id="module" class="form-control" @bind="@_location" readonly />
</div> </div>
</div> </div>
} }
</div> </div>
</form>
<button type="button" class="btn btn-success" @onclick="CreateModule">@Localizer["Module.Create"]</button> <button type="button" class="btn btn-success" @onclick="CreateModule">@Localizer["Module.Create"]</button>
} }
else else
{ {
<button type="button" class="btn btn-success" @onclick="ActivateModule">@Localizer["Module.Activate"]</button> <button type="button" class="btn btn-success" @onclick="ActivateModule">@Localizer["Module.Activate"]</button>
</form>
} }
@code { @code {
@ -92,22 +91,25 @@
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host; 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() protected override async Task OnParametersSetAsync()
{ {
try try
{ {
_moduledefinitionname = SettingService.GetSetting(ModuleState.Settings, "ModuleDefinitionName", "");
_templates = await ModuleDefinitionService.GetModuleDefinitionTemplatesAsync(); _templates = await ModuleDefinitionService.GetModuleDefinitionTemplatesAsync();
_versions = Constants.ReleaseVersions.Split(',').Where(item => Version.Parse(item).CompareTo(Version.Parse("2.0.0")) >= 0).ToArray(); _versions = Constants.ReleaseVersions.Split(',').Where(item => Version.Parse(item).CompareTo(Version.Parse("2.0.0")) >= 0).ToArray();
if (string.IsNullOrEmpty(_moduledefinitionname))
{
AddModuleMessage(Localizer["Info.Module.Creator"], MessageType.Info);
}
else
{
AddModuleMessage(Localizer["Info.Module.Activate"], MessageType.Info);
}
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -131,7 +133,6 @@
await SettingService.UpdateModuleSettingsAsync(settings, ModuleState.ModuleId); await SettingService.UpdateModuleSettingsAsync(settings, ModuleState.ModuleId);
GetLocation(); GetLocation();
AddModuleMessage(string.Format(Localizer["Success.Module.Create"], NavigateUrl("admin/system")), MessageType.Success); AddModuleMessage(string.Format(Localizer["Success.Module.Create"], NavigateUrl("admin/system")), MessageType.Success);
} }
catch (Exception ex) catch (Exception ex)

View File

@ -1,7 +1,9 @@
using Oqtane.Models; using Oqtane.Documentation;
using Oqtane.Models;
namespace Oqtane.Modules.Admin.ModuleCreator 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 class ModuleInfo : IModule
{ {
public ModuleDefinition ModuleDefinition => new ModuleDefinition public ModuleDefinition ModuleDefinition => new ModuleDefinition

View File

@ -70,7 +70,7 @@
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" HelpText="Upload one or more module packages. Once they are uploaded click Install to complete the installation." ResourceKey="Module">Module: </Label> <Label Class="col-sm-3" HelpText="Upload one or more module packages. Once they are uploaded click Install to complete the installation." ResourceKey="Module">Module: </Label>
<div class="col-sm-9"> <div class="col-sm-9">
<FileManager Filter="nupkg" ShowFiles="false" Folder="Packages" UploadMultiple="true" /> <FileManager Folder="@Constants.PackagesFolder" UploadMultiple="true" />
</div> </div>
</div> </div>
</div> </div>
@ -229,7 +229,7 @@
{ {
try try
{ {
await PackageService.DownloadPackageAsync(_packageid, _version, "Packages"); await PackageService.DownloadPackageAsync(_packageid, _version, Constants.PackagesFolder);
await logger.LogInformation("Package {PackageId} {Version} Downloaded Successfully", _packageid, _version); await logger.LogInformation("Package {PackageId} {Version} Downloaded Successfully", _packageid, _version);
AddModuleMessage(Localizer["Success.Module.Download"], MessageType.Success); AddModuleMessage(Localizer["Success.Module.Download"], MessageType.Success);
_productname = ""; _productname = "";

View File

@ -87,13 +87,17 @@
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
protected override void OnInitialized()
{
AddModuleMessage(Localizer["Info.Module.Development"], MessageType.Info);
}
protected override async Task OnParametersSetAsync() protected override async Task OnParametersSetAsync()
{ {
try try
{ {
_templates = await ModuleDefinitionService.GetModuleDefinitionTemplatesAsync(); _templates = await ModuleDefinitionService.GetModuleDefinitionTemplatesAsync();
_versions = Constants.ReleaseVersions.Split(',').Where(item => Version.Parse(item).CompareTo(Version.Parse("2.0.0")) >= 0).ToArray(); _versions = Constants.ReleaseVersions.Split(',').Where(item => Version.Parse(item).CompareTo(Version.Parse("2.0.0")) >= 0).ToArray();
AddModuleMessage(Localizer["Info.Module.Development"], MessageType.Info);
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@ -22,6 +22,7 @@ else
<th style="width: 1px;">&nbsp;</th> <th style="width: 1px;">&nbsp;</th>
<th>@SharedLocalizer["Name"]</th> <th>@SharedLocalizer["Name"]</th>
<th>@SharedLocalizer["Version"]</th> <th>@SharedLocalizer["Version"]</th>
<th>@Localizer["InUse"]</th>
<th>@SharedLocalizer["Expires"]</th> <th>@SharedLocalizer["Expires"]</th>
<th style="width: 1px;">&nbsp;</th> <th style="width: 1px;">&nbsp;</th>
</Header> </Header>
@ -35,6 +36,16 @@ else
</td> </td>
<td>@context.Name</td> <td>@context.Name</td>
<td>@context.Version</td> <td>@context.Version</td>
<td>
@if(context.AssemblyName == "Oqtane.Client" || PageState.Modules.Where(m => m.ModuleDefinition.ModuleDefinitionId == context.ModuleDefinitionId).Count() > 0)
{
<span>@SharedLocalizer["Yes"]</span>
}
else
{
<span>@SharedLocalizer["No"]</span>
}
</td>
<td> <td>
@((MarkupString)PurchaseLink(context.PackageName)) @((MarkupString)PurchaseLink(context.PackageName))
</td> </td>
@ -111,7 +122,7 @@ else
{ {
try try
{ {
await PackageService.DownloadPackageAsync(packagename, version, "Packages"); await PackageService.DownloadPackageAsync(packagename, version, Constants.PackagesFolder);
await logger.LogInformation("Module Downloaded {ModuleDefinitionName} {Version}", packagename, version); await logger.LogInformation("Module Downloaded {ModuleDefinitionName} {Version}", packagename, version);
await ModuleDefinitionService.InstallModuleDefinitionsAsync(); await ModuleDefinitionService.InstallModuleDefinitionsAsync();
AddModuleMessage(string.Format(Localizer["Success.Module.Install"], NavigateUrl("admin/system")), MessageType.Success); AddModuleMessage(string.Format(Localizer["Success.Module.Install"], NavigateUrl("admin/system")), MessageType.Success);

View File

@ -74,7 +74,7 @@
</div> </div>
</div> </div>
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="path" HelpText="Optionally enter a url path for this page (ie. home ). If you do not provide a url path, the page name will be used." ResourceKey="UrlPath">Url Path: </Label> <Label Class="col-sm-3" For="path" HelpText="Optionally enter a url path for this page (ie. home ). If you do not provide a url path, the page name will be used. If the page is intended to be the root path specify '/'." ResourceKey="UrlPath">Url Path: </Label>
<div class="col-sm-9"> <div class="col-sm-9">
<input id="path" class="form-control" @bind="@_path" /> <input id="path" class="form-control" @bind="@_path" />
</div> </div>
@ -293,11 +293,11 @@
page.SiteId = PageState.Page.SiteId; page.SiteId = PageState.Page.SiteId;
page.Name = _name; page.Name = _name;
page.Title = _title; page.Title = _title;
if (string.IsNullOrEmpty(_path)) if (string.IsNullOrEmpty(_path))
{ {
_path = _name; _path = _name;
} }
if (_path.Contains("/")) if (_path.Contains("/"))
{ {
_path = _path.Substring(_path.LastIndexOf("/") + 1); _path = _path.Substring(_path.LastIndexOf("/") + 1);

View File

@ -3,13 +3,14 @@
@inherits ModuleBase @inherits ModuleBase
@inject NavigationManager NavigationManager @inject NavigationManager NavigationManager
@inject IPageService PageService @inject IPageService PageService
@inject IPageModuleService PageModuleService
@inject IThemeService ThemeService @inject IThemeService ThemeService
@inject IStringLocalizer<Edit> Localizer @inject IStringLocalizer<Edit> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer @inject IStringLocalizer<SharedResources> SharedLocalizer
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate> <form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
<TabStrip Refresh="@_refresh"> <TabStrip Refresh="@_refresh">
<TabPanel Name="Settings" ResourceKey="Settings"> <TabPanel Name="Settings" ResourceKey="Settings" Heading=@Localizer["Settings.Heading"]>
@if (_themeList != null) @if (_themeList != null)
{ {
<div class="container"> <div class="container">
@ -81,7 +82,7 @@
</div> </div>
</div> </div>
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="path" HelpText="Optionally enter a url path for this page (ie. home ). If you do not provide a url path, the page name will be used." ResourceKey="UrlPath">Url Path: </Label> <Label Class="col-sm-3" For="path" HelpText="Optionally enter a url path for this page (ie. home ). If you do not provide a url path, the page name will be used. If the page is intended to be the root path specify '/'." ResourceKey="UrlPath">Url Path: </Label>
<div class="col-sm-9"> <div class="col-sm-9">
<input id="path" class="form-control" @bind="@_path" maxlength="256"/> <input id="path" class="form-control" @bind="@_path" maxlength="256"/>
</div> </div>
@ -151,10 +152,27 @@
<div class="container"> <div class="container">
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<PermissionGrid EntityName="@EntityNames.Page" Permissions="@_permissions" @ref="_permissionGrid" /> <PermissionGrid EntityName="@EntityNames.Page" Permissions="@_permissions" @ref="_permissionGrid" />
</div> </div>
</div> </div>
}
</TabPanel>
<TabPanel Name="PageModules" Heading="Modules" ResourceKey="PageModules">
@if(_pageModules != null)
{
<Pager Items="_pageModules">
<Header>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>@Localizer["ModuleTitle"]</th>
<th>@Localizer["ModuleDefinition"]</th>
</Header>
<Row>
<td><ActionLink Action="Settings" Text="Edit" ModuleId="@context.ModuleId" Security="SecurityAccessLevel.Edit" Permissions="@context.Permissions" ResourceKey="ModuleSettings" /></td>
<td><ActionDialog Header="Delete Module" Message="Are You Sure You Wish To Delete This Module?" Action="Delete" Security="SecurityAccessLevel.Edit" Permissions="@context.Permissions" Class="btn btn-danger" OnClick="@(async () => await DeleteModule(context))" ResourceKey="DeleteModule" /></td>
<td>@context.Title</td>
<td>@context.ModuleDefinition?.Name</td>
</Row>
</Pager>
} }
</TabPanel> </TabPanel>
@if (_themeSettingsType != null) @if (_themeSettingsType != null)
@ -170,107 +188,136 @@
</form> </form>
@code { @code {
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
private ElementReference form; private ElementReference form;
private bool validated = false; private bool validated = false;
private List<Theme> _themeList; private List<Theme> _themeList;
private List<ThemeControl> _themes = new List<ThemeControl>(); private List<ThemeControl> _themes = new List<ThemeControl>();
private List<ThemeControl> _containers = new List<ThemeControl>(); private List<ThemeControl> _containers = new List<ThemeControl>();
private List<Page> _pageList; private List<Page> _pageList;
private int _pageId; private List<Module> _pageModules;
private string _name; private int _pageId;
private string _title; private string _name;
private string _path; private string _title;
private string _currentparentid; private string _path;
private string _parentid = "-1"; private string _currentparentid;
private string _insert = "="; private string _parentid = "-1";
private List<Page> _children; private string _insert = "=";
private int _childid = -1; private List<Page> _children;
private string _isnavigation; private int _childid = -1;
private string _isclickable; private string _isnavigation;
private string _url; private string _isclickable;
private string _ispersonalizable; private string _url;
private string _themetype; private string _ispersonalizable;
private string _containertype = "-"; private string _themetype;
private string _icon; private string _containertype = "-";
private string _permissions = null; private string _icon;
private string _createdby; private string _permissions = null;
private DateTime _createdon; private string _createdby;
private string _modifiedby; private DateTime _createdon;
private DateTime _modifiedon; private string _modifiedby;
private string _deletedby; private DateTime _modifiedon;
private DateTime? _deletedon; private string _deletedby;
private PermissionGrid _permissionGrid; private DateTime? _deletedon;
private Type _themeSettingsType; private PermissionGrid _permissionGrid;
private object _themeSettings; private Type _themeSettingsType;
private RenderFragment ThemeSettingsComponent { get; set; } private object _themeSettings;
private bool _refresh = false; private RenderFragment ThemeSettingsComponent { get; set; }
private bool _refresh = false;
protected Page page;
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
try try
{ {
_pageList = PageState.Pages; _pageList = PageState.Pages;
_children = PageState.Pages.Where(item => item.ParentId == null).ToList(); _children = PageState.Pages.Where(item => item.ParentId == null).ToList();
_themeList = await ThemeService.GetThemesAsync();
_themes = ThemeService.GetThemeControls(_themeList);
_themeList = await ThemeService.GetThemesAsync(); _pageId = Int32.Parse(PageState.QueryString["id"]);
_themes = ThemeService.GetThemeControls(_themeList); page = PageState.Pages.FirstOrDefault(item => item.PageId == _pageId);
_pageId = Int32.Parse(PageState.QueryString["id"]); if (page != null)
var page = PageState.Pages.FirstOrDefault(item => item.PageId == _pageId); {
if (page != null) _name = page.Name;
{ _title = page.Title;
_name = page.Name; _path = page.Path;
_title = page.Title; _pageModules = PageState.Modules.Where(m => m.PageId == page.PageId && m.IsDeleted == false).ToList();
_path = page.Path;
if (_path.Contains("/")) if (string.IsNullOrEmpty(_path))
{ {
_path = _path.Substring(_path.LastIndexOf("/") + 1); _path = "/";
} }
else
{
if (_path.Contains("/"))
{
_path = _path.Substring(_path.LastIndexOf("/") + 1);
}
}
if (page.ParentId == null) if (page.ParentId == null)
{ {
_parentid = "-1"; _parentid = "-1";
} }
else else
{ {
_parentid = page.ParentId.ToString(); _parentid = page.ParentId.ToString();
} }
_currentparentid = _parentid; _currentparentid = _parentid;
_isnavigation = page.IsNavigation.ToString(); _isnavigation = page.IsNavigation.ToString();
_isclickable = page.IsClickable.ToString(); _isclickable = page.IsClickable.ToString();
_url = page.Url; _url = page.Url;
_ispersonalizable = page.IsPersonalizable.ToString(); _ispersonalizable = page.IsPersonalizable.ToString();
_themetype = page.ThemeType; _themetype = page.ThemeType;
if (string.IsNullOrEmpty(_themetype)) if (string.IsNullOrEmpty(_themetype))
{ {
_themetype = PageState.Site.DefaultThemeType; _themetype = PageState.Site.DefaultThemeType;
} }
_containers = ThemeService.GetContainerControls(_themeList, _themetype); _containers = ThemeService.GetContainerControls(_themeList, _themetype);
_containertype = page.DefaultContainerType; _containertype = page.DefaultContainerType;
if (string.IsNullOrEmpty(_containertype)) if (string.IsNullOrEmpty(_containertype))
{ {
_containertype = PageState.Site.DefaultContainerType; _containertype = PageState.Site.DefaultContainerType;
} }
_icon = page.Icon; _icon = page.Icon;
_permissions = page.Permissions; _permissions = page.Permissions;
_createdby = page.CreatedBy; _createdby = page.CreatedBy;
_createdon = page.CreatedOn; _createdon = page.CreatedOn;
_modifiedby = page.ModifiedBy; _modifiedby = page.ModifiedBy;
_modifiedon = page.ModifiedOn; _modifiedon = page.ModifiedOn;
_deletedby = page.DeletedBy; _deletedby = page.DeletedBy;
_deletedon = page.DeletedOn; _deletedon = page.DeletedOn;
ThemeSettings(); ThemeSettings();
} }
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading Page {PageId} {Error}", _pageId, ex.Message);
AddModuleMessage(Localizer["Error.Page.Load"], MessageType.Error);
}
}
private async Task DeleteModule(Module module)
{
try
{
PageModule pagemodule = await PageModuleService.GetPageModuleAsync(page.PageId, module.ModuleId);
pagemodule.IsDeleted = true;
await PageModuleService.UpdatePageModuleAsync(pagemodule);
await logger.LogInformation(LogFunction.Update,"Module Deleted {Title}", module.Title);
_pageModules.RemoveAll(item => item.PageModuleId == pagemodule.PageModuleId);
StateHasChanged();
NavigationManager.NavigateTo(NavigationManager.Uri + "&tab=PageModules");
} }
catch (Exception ex) catch (Exception ex)
{ {
await logger.LogError(ex, "Error Loading Page {PageId} {Error}", _pageId, ex.Message); await logger.LogError(ex, "Error Deleting Module {Title} {Error}", module.Title, ex.Message);
AddModuleMessage(Localizer["Error.Page.Load"], MessageType.Error); AddModuleMessage(Localizer["Error.Module.Delete"], MessageType.Error);
} }
} }
@ -370,7 +417,8 @@
page.Name = _name; page.Name = _name;
page.Title = _title; page.Title = _title;
if (string.IsNullOrEmpty(_path) && _name.ToLower() != "home")
if (string.IsNullOrEmpty(_path))
{ {
_path = _name; _path = _name;
} }
@ -378,6 +426,7 @@
{ {
_path = _path.Substring(_path.LastIndexOf("/") + 1); _path = _path.Substring(_path.LastIndexOf("/") + 1);
} }
if (_parentid == "-1") if (_parentid == "-1")
{ {
page.ParentId = null; page.ParentId = null;

View File

@ -11,13 +11,14 @@
<Pager Items="@PageState.Pages.Where(item => !item.IsDeleted)"> <Pager Items="@PageState.Pages.Where(item => !item.IsDeleted)">
<Header> <Header>
<th style="width: 1px;">&nbsp;</th> <th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th> <th style="width: 1px;">&nbsp;</th>
<th>@SharedLocalizer["Name"]</th> <th>@SharedLocalizer["Name"]</th>
</Header> </Header>
<Row> <Row>
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.PageId.ToString())" ResourceKey="EditPage" /></td> <td><ActionLink Action="Edit" Parameters="@($"id=" + context.PageId.ToString())" ResourceKey="EditPage" /></td>
<td><ActionDialog Header="Delete Page" Message="@string.Format(Localizer["Confirm.Page.Delete"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeletePage(context))" ResourceKey="DeletePage" /></td> <td><ActionDialog Header="Delete Page" Message="@string.Format(Localizer["Confirm.Page.Delete"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeletePage(context))" ResourceKey="DeletePage" /></td>
<td><button type="button" class="btn btn-secondary" @onclick="@(async () => NavigationManager.NavigateTo(Browse(context)))">@Localizer["Browse"]</button></td>
<td>@(new string('-', context.Level * 2))@(context.Name)</td> <td>@(new string('-', context.Level * 2))@(context.Name)</td>
</Row> </Row>
</Pager> </Pager>
@ -42,4 +43,8 @@
AddModuleMessage(Localizer["Error.Page.Delete"], MessageType.Error); AddModuleMessage(Localizer["Error.Page.Delete"], MessageType.Error);
} }
} }
protected string Browse(Page page)
{
return string.IsNullOrEmpty(page.Url) ? NavigateUrl(page.Path) : page.Url;
}
} }

View File

@ -25,7 +25,7 @@
<th>@Localizer["DeletedOn"]</th> <th>@Localizer["DeletedOn"]</th>
</Header> </Header>
<Row> <Row>
<td><button @onclick="@(() => RestorePage(context))" class="btn btn-info" title="Restore">Restore</button></td> <td><button type="button" @onclick="@(() => RestorePage(context))" class="btn btn-info" title="Restore">Restore</button></td>
<td><ActionDialog Header="Delete Page" Message="@string.Format(Localizer["Confirm.Page.Delete"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeletePage(context))" ResourceKey="DeletePage" /></td> <td><ActionDialog Header="Delete Page" Message="@string.Format(Localizer["Confirm.Page.Delete"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeletePage(context))" ResourceKey="DeletePage" /></td>
<td>@context.Name</td> <td>@context.Name</td>
<td>@context.DeletedBy</td> <td>@context.DeletedBy</td>
@ -34,9 +34,7 @@
</Pager> </Pager>
@if (_pages.Any()) @if (_pages.Any())
{ {
<div style="text-align:right;"> <br /><ActionDialog Header="Delete All Pages" Message="Are You Sure You Wish To Permanently Delete All Pages?" Action="Delete All Pages" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteAllPages())" ResourceKey="DeleteAllPages" />
<ActionDialog Header="Delete All Pages" Message="Are You Sure You Wish To Permanently Delete All Pages?" Action="Delete All Pages" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteAllPages())" ResourceKey="DeleteAllPages" />
</div>
} }
} }
</TabPanel> </TabPanel>
@ -58,7 +56,7 @@
<th>@Localizer["DeletedOn"]</th> <th>@Localizer["DeletedOn"]</th>
</Header> </Header>
<Row> <Row>
<td><button @onclick="@(() => RestoreModule(context))" class="btn btn-info" title="Restore">@Localizer["Restore"]</button></td> <td><button type="button" @onclick="@(() => RestoreModule(context))" class="btn btn-info" title="Restore">@Localizer["Restore"]</button></td>
<td><ActionDialog Header="Delete Module" Message="@string.Format(Localizer["Confirm.Module.Delete"], context.Title)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteModule(context))" ResourceKey="DeleteModule" /></td> <td><ActionDialog Header="Delete Module" Message="@string.Format(Localizer["Confirm.Module.Delete"], context.Title)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteModule(context))" ResourceKey="DeleteModule" /></td>
<td>@PageState.Pages.Find(item => item.PageId == context.PageId).Name</td> <td>@PageState.Pages.Find(item => item.PageId == context.PageId).Name</td>
<td>@context.Title</td> <td>@context.Title</td>
@ -68,9 +66,7 @@
</Pager> </Pager>
@if (_modules.Any()) @if (_modules.Any())
{ {
<div style="text-align:right;"> <br /><ActionDialog Header="Delete All Modules" Message="Are You Sure You Wish To Permanently Delete All Modules?" Action="Delete All Modules" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteAllModules())" ResourceKey="DeleteAllModules" />
<ActionDialog Header="Delete All Modules" Message="Are You Sure You Wish To Permanently Delete All Modules?" Action="Delete All Modules" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteAllModules())" ResourceKey="DeleteAllModules" />
</div>
} }
} }

View File

@ -35,13 +35,13 @@ else
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="effectiveDate" HelpText="The date that this role assignment is active" ResourceKey="EffectiveDate">Effective Date: </Label> <Label Class="col-sm-3" For="effectiveDate" HelpText="The date that this role assignment is active" ResourceKey="EffectiveDate">Effective Date: </Label>
<div class="col-sm-9"> <div class="col-sm-9">
<input type="date" id="effectiveDate" class="form-control" @bind="@effectivedate" required /> <input type="date" id="effectiveDate" class="form-control" @bind="@effectivedate" />
</div> </div>
</div> </div>
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="expiryDate" HelpText="The date that this role assignment expires" ResourceKey="ExpiryDate">Expiry Date: </Label> <Label Class="col-sm-3" For="expiryDate" HelpText="The date that this role assignment expires" ResourceKey="ExpiryDate">Expiry Date: </Label>
<div class="col-sm-9"> <div class="col-sm-9">
<input type="date" id="expiryDate" class="form-control" @bind="@expirydate" required /> <input type="date" id="expiryDate" class="form-control" @bind="@expirydate" />
</div> </div>
</div> </div>
<br /><br /> <br /><br />

View File

@ -34,15 +34,6 @@
} }
</div> </div>
</div> </div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="allowRegister" HelpText="Do you want the users to be able to register for an account on the site" ResourceKey="AllowRegistration">Allow User Registration? </Label>
<div class="col-sm-9">
<select id="allowRegister" class="form-select" @bind="@_allowregistration" required>
<option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option>
</select>
</div>
</div>
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="isDeleted" HelpText="Is this site deleted?" ResourceKey="IsDeleted">Is Deleted? </Label> <Label Class="col-sm-3" For="isDeleted" HelpText="Is this site deleted?" ResourceKey="IsDeleted">Is Deleted? </Label>
<div class="col-sm-9"> <div class="col-sm-9">
@ -104,7 +95,6 @@
</select> </select>
</div> </div>
</div> </div>
</div> </div>
</Section> </Section>
<Section Name="SMTP" Heading="SMTP Settings" ResourceKey="SMTPSettings"> <Section Name="SMTP" Heading="SMTP Settings" ResourceKey="SMTPSettings">
@ -157,10 +147,7 @@
</div> </div>
<button type="button" class="btn btn-secondary" @onclick="SendEmail">@Localizer["Smtp.TestConfig"]</button> <button type="button" class="btn btn-secondary" @onclick="SendEmail">@Localizer["Smtp.TestConfig"]</button>
<br /><br /> <br /><br />
</div> </div>
</Section> </Section>
<Section Name="PWA" Heading="Progressive Web Application Settings" ResourceKey="PWASettings"> <Section Name="PWA" Heading="Progressive Web Application Settings" ResourceKey="PWASettings">
<div class="container"> <div class="container">
@ -189,6 +176,28 @@
</Section> </Section>
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host)) @if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
{ {
<Section Name="Hosting" Heading="Hosting Model" ResourceKey="Hosting">
<div class="container">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="runtime" HelpText="The Blazor runtime hosting model" ResourceKey="Runtime">Runtime: </Label>
<div class="col-sm-9">
<select id="runtime" class="form-select" @bind="@_runtime" required>
<option value="Server">@SharedLocalizer["BlazorServer"]</option>
<option value="WebAssembly">@SharedLocalizer["BlazorWebAssembly"]</option>
</select>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="prerender" HelpText="Specifies if the site should be prerendered (for search crawlers, etc...)" ResourceKey="Prerender">Prerender? </Label>
<div class="col-sm-9">
<select id="prerender" class="form-select" @bind="@_prerender" required>
<option value="Prerendered">@SharedLocalizer["Yes"]</option>
<option value="">@SharedLocalizer["No"]</option>
</select>
</div>
</div>
</div>
</Section>
<Section Name="TenantInformation" Heading="Tenant Information" ResourceKey="TenantInformation"> <Section Name="TenantInformation" Heading="Tenant Information" ResourceKey="TenantInformation">
<div class="container"> <div class="container">
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
@ -222,228 +231,246 @@
} }
@code { @code {
private ElementReference form; private ElementReference form;
private bool validated = false; private bool validated = false;
private bool _initialized = false; private bool _initialized = false;
private List<Theme> _themeList; private List<Theme> _themeList;
private List<ThemeControl> _themes = new List<ThemeControl>(); private List<ThemeControl> _themes = new List<ThemeControl>();
private List<ThemeControl> _containers = new List<ThemeControl>(); private List<ThemeControl> _containers = new List<ThemeControl>();
private string _name = string.Empty; private string _name = string.Empty;
private List<Alias> _aliasList; private List<Alias> _aliasList;
private string _urls = string.Empty; private string _urls = string.Empty;
private int _logofileid = -1; private string _runtime = "";
private FileManager _logofilemanager; private string _prerender = "";
private int _faviconfileid = -1; private int _logofileid = -1;
private FileManager _faviconfilemanager; private FileManager _logofilemanager;
private string _themetype = "-"; private int _faviconfileid = -1;
private string _containertype = "-"; private FileManager _faviconfilemanager;
private string _admincontainertype = "-"; private string _themetype = "-";
private string _allowregistration; private string _containertype = "-";
private string _smtphost = string.Empty; private string _admincontainertype = "-";
private string _smtpport = string.Empty; private string _smtphost = string.Empty;
private string _smtpssl = "False"; private string _smtpport = string.Empty;
private string _smtpusername = string.Empty; private string _smtpssl = "False";
private string _smtppassword = string.Empty; private string _smtpusername = string.Empty;
private string _smtpsender = string.Empty; private string _smtppassword = string.Empty;
private string _pwaisenabled; private string _smtpsender = string.Empty;
private int _pwaappiconfileid = -1; private string _pwaisenabled;
private FileManager _pwaappiconfilemanager; private int _pwaappiconfileid = -1;
private int _pwasplashiconfileid = -1; private FileManager _pwaappiconfilemanager;
private FileManager _pwasplashiconfilemanager; private int _pwasplashiconfileid = -1;
private string _tenant = string.Empty; private FileManager _pwasplashiconfilemanager;
private string _database = string.Empty; private string _tenant = string.Empty;
private string _connectionstring = string.Empty; private string _database = string.Empty;
private string _createdby; private string _connectionstring = string.Empty;
private DateTime _createdon; private string _createdby;
private string _modifiedby; private DateTime _createdon;
private DateTime _modifiedon; private string _modifiedby;
private string _deletedby; private DateTime _modifiedon;
private DateTime? _deletedon; private string _deletedby;
private string _isdeleted; private DateTime? _deletedon;
private string _isdeleted;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
try try
{ {
_themeList = await ThemeService.GetThemesAsync(); _themeList = await ThemeService.GetThemesAsync();
Site site = await SiteService.GetSiteAsync(PageState.Site.SiteId); Site site = await SiteService.GetSiteAsync(PageState.Site.SiteId);
if (site != null) if (site != null)
{ {
_name = site.Name; _name = site.Name;
_allowregistration = site.AllowRegistration.ToString(); _runtime = site.Runtime;
_isdeleted = site.IsDeleted.ToString(); _prerender = site.RenderMode.Replace(_runtime, "");
_isdeleted = site.IsDeleted.ToString();
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host)) if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
{ {
_aliasList = await AliasService.GetAliasesAsync(); _aliasList = await AliasService.GetAliasesAsync();
foreach (Alias alias in _aliasList.Where(item => item.SiteId == site.SiteId && item.TenantId == site.TenantId).ToList()) foreach (Alias alias in _aliasList.Where(item => item.SiteId == site.SiteId && item.TenantId == site.TenantId).ToList())
{ {
_urls += alias.Name + ","; _urls += alias.Name + ",";
} }
_urls = _urls.Substring(0, _urls.Length - 1); _urls = _urls.Substring(0, _urls.Length - 1);
} }
if (site.LogoFileId != null) if (site.LogoFileId != null)
{ {
_logofileid = site.LogoFileId.Value; _logofileid = site.LogoFileId.Value;
} }
if (site.FaviconFileId != null) if (site.FaviconFileId != null)
{ {
_faviconfileid = site.FaviconFileId.Value; _faviconfileid = site.FaviconFileId.Value;
} }
_themes = ThemeService.GetThemeControls(_themeList); _themes = ThemeService.GetThemeControls(_themeList);
_themetype = (!string.IsNullOrEmpty(site.DefaultThemeType)) ? site.DefaultThemeType : Constants.DefaultTheme; _themetype = (!string.IsNullOrEmpty(site.DefaultThemeType)) ? site.DefaultThemeType : Constants.DefaultTheme;
_containers = ThemeService.GetContainerControls(_themeList, _themetype); _containers = ThemeService.GetContainerControls(_themeList, _themetype);
_containertype = (!string.IsNullOrEmpty(site.DefaultContainerType)) ? site.DefaultContainerType : Constants.DefaultContainer; _containertype = (!string.IsNullOrEmpty(site.DefaultContainerType)) ? site.DefaultContainerType : Constants.DefaultContainer;
_admincontainertype = (!string.IsNullOrEmpty(site.AdminContainerType)) ? site.AdminContainerType : Constants.DefaultAdminContainer; _admincontainertype = (!string.IsNullOrEmpty(site.AdminContainerType)) ? site.AdminContainerType : Constants.DefaultAdminContainer;
var settings = await SettingService.GetSiteSettingsAsync(site.SiteId); _pwaisenabled = site.PwaIsEnabled.ToString();
_smtphost = SettingService.GetSetting(settings, "SMTPHost", string.Empty); if (site.PwaAppIconFileId != null)
_smtpport = SettingService.GetSetting(settings, "SMTPPort", string.Empty); {
_smtpssl = SettingService.GetSetting(settings, "SMTPSSL", "False"); _pwaappiconfileid = site.PwaAppIconFileId.Value;
_smtpusername = SettingService.GetSetting(settings, "SMTPUsername", string.Empty); }
_smtppassword = SettingService.GetSetting(settings, "SMTPPassword", string.Empty); if (site.PwaSplashIconFileId != null)
_smtpsender = SettingService.GetSetting(settings, "SMTPSender", string.Empty); {
_pwasplashiconfileid = site.PwaSplashIconFileId.Value;
}
var settings = await SettingService.GetSiteSettingsAsync(site.SiteId);
_smtphost = SettingService.GetSetting(settings, "SMTPHost", string.Empty);
_smtpport = SettingService.GetSetting(settings, "SMTPPort", string.Empty);
_smtpssl = SettingService.GetSetting(settings, "SMTPSSL", "False");
_smtpusername = SettingService.GetSetting(settings, "SMTPUsername", string.Empty);
_smtppassword = SettingService.GetSetting(settings, "SMTPPassword", string.Empty);
_smtpsender = SettingService.GetSetting(settings, "SMTPSender", string.Empty);
_pwaisenabled = site.PwaIsEnabled.ToString(); if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
{
var tenants = await TenantService.GetTenantsAsync();
var _databases = await DatabaseService.GetDatabasesAsync();
var tenant = tenants.Find(item => item.TenantId == site.TenantId);
if (tenant != null)
{
_tenant = tenant.Name;
_database = _databases.Find(item => item.DBType == tenant.DBType)?.Name;
_connectionstring = tenant.DBConnectionString;
}
}
if (site.PwaAppIconFileId != null) _createdby = site.CreatedBy;
{ _createdon = site.CreatedOn;
_pwaappiconfileid = site.PwaAppIconFileId.Value; _modifiedby = site.ModifiedBy;
} _modifiedon = site.ModifiedOn;
_deletedby = site.DeletedBy;
_deletedon = site.DeletedOn;
if (site.PwaSplashIconFileId != null) _initialized = true;
{ }
_pwasplashiconfileid = site.PwaSplashIconFileId.Value; }
} catch (Exception ex)
{
await logger.LogError(ex, "Error Loading Site {SiteId} {Error}", PageState.Site.SiteId, ex.Message);
AddModuleMessage(ex.Message, MessageType.Error);
}
}
_pwaisenabled = site.PwaIsEnabled.ToString(); private async void ThemeChanged(ChangeEventArgs e)
if (site.PwaAppIconFileId != null) {
{ try
_pwaappiconfileid = site.PwaAppIconFileId.Value; {
} _themetype = (string)e.Value;
if (site.PwaSplashIconFileId != null) if (_themetype != "-")
{ {
_pwasplashiconfileid = site.PwaSplashIconFileId.Value; _containers = ThemeService.GetContainerControls(_themeList, _themetype);
} }
else
{
_containers = new List<ThemeControl>();
}
_containertype = "-";
_admincontainertype = Constants.DefaultAdminContainer;
StateHasChanged();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading Pane Layouts For Theme {ThemeType} {Error}", _themetype, ex.Message);
AddModuleMessage(Localizer["Error.Theme.LoadPane"], MessageType.Error);
}
}
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host)) private async Task SaveSite()
{ {
var tenants = await TenantService.GetTenantsAsync(); validated = true;
var _databases = await DatabaseService.GetDatabasesAsync(); var interop = new Interop(JSRuntime);
var tenant = tenants.Find(item => item.TenantId == site.TenantId); if (await interop.FormValid(form))
if (tenant != null) {
{ try
_tenant = tenant.Name; {
_database = _databases.Find(item => item.DBType == tenant.DBType)?.Name; if (_name != string.Empty && _urls != string.Empty && _themetype != "-" && _containertype != "-")
_connectionstring = tenant.DBConnectionString; {
} var unique = true;
} if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
{
foreach (string name in _urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
if (_aliasList.Exists(item => item.Name == name && item.SiteId != PageState.Alias.SiteId && item.TenantId != PageState.Alias.TenantId))
{
unique = false;
}
}
}
_createdby = site.CreatedBy; if (unique)
_createdon = site.CreatedOn; {
_modifiedby = site.ModifiedBy; var site = await SiteService.GetSiteAsync(PageState.Site.SiteId);
_modifiedon = site.ModifiedOn; if (site != null)
_deletedby = site.DeletedBy; {
_deletedon = site.DeletedOn; bool refresh = false;
bool reload = false;
_initialized = true; site.Name = _name;
} if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
} {
catch (Exception ex) if (site.Runtime != _runtime || site.RenderMode != _runtime + _prerender)
{ {
await logger.LogError(ex, "Error Loading Site {SiteId} {Error}", PageState.Site.SiteId, ex.Message); site.Runtime = _runtime;
AddModuleMessage(ex.Message, MessageType.Error); site.RenderMode = _runtime + _prerender;
} refresh = true;
} reload = true; // needs to be reloaded on server
}
}
site.IsDeleted = (_isdeleted == null ? true : Boolean.Parse(_isdeleted));
private async void ThemeChanged(ChangeEventArgs e) site.LogoFileId = null;
{ var logofileid = _logofilemanager.GetFileId();
try if (logofileid != -1)
{ {
_themetype = (string)e.Value; site.LogoFileId = logofileid;
if (_themetype != "-") }
{ int? faviconFieldId = _faviconfilemanager.GetFileId();
_containers = ThemeService.GetContainerControls(_themeList, _themetype); if (faviconFieldId == -1) faviconFieldId = null;
} if (site.FaviconFileId != faviconFieldId)
else {
{ site.FaviconFileId = faviconFieldId;
_containers = new List<ThemeControl>(); reload = true; // needs to be reloaded on server
} }
_containertype = "-"; if (site.DefaultThemeType != _themetype)
_admincontainertype = Constants.DefaultAdminContainer; {
StateHasChanged(); site.DefaultThemeType = _themetype;
} refresh = true; // needs to be refreshed on client
catch (Exception ex) }
{ if (site.DefaultContainerType != _containertype)
await logger.LogError(ex, "Error Loading Pane Layouts For Theme {ThemeType} {Error}", _themetype, ex.Message); {
AddModuleMessage(Localizer["Error.Theme.LoadPane"], MessageType.Error); site.DefaultContainerType = _containertype;
} refresh = true; // needs to be refreshed on client
} }
site.AdminContainerType = _admincontainertype;
private async Task SaveSite() if (site.PwaIsEnabled.ToString() != _pwaisenabled)
{ {
validated = true; site.PwaIsEnabled = Boolean.Parse(_pwaisenabled);
var interop = new Interop(JSRuntime); reload = true; // needs to be reloaded on server
if (await interop.FormValid(form)) }
{ int? pwaappiconfileid = _pwaappiconfilemanager.GetFileId();
try if (pwaappiconfileid == -1) pwaappiconfileid = null;
{ if (site.PwaAppIconFileId != pwaappiconfileid)
if (_name != string.Empty && _urls != string.Empty && _themetype != "-" && _containertype != "-")
{
var unique = true;
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
{
foreach (string name in _urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
if (_aliasList.Exists(item => item.Name == name && item.SiteId != PageState.Alias.SiteId && item.TenantId != PageState.Alias.TenantId))
{
unique = false;
}
}
}
if (unique)
{
var site = await SiteService.GetSiteAsync(PageState.Site.SiteId);
if (site != null)
{
bool refresh = (site.DefaultThemeType != _themetype || site.DefaultContainerType != _containertype);
site.Name = _name;
site.AllowRegistration = (_allowregistration == null ? true : Boolean.Parse(_allowregistration));
site.IsDeleted = (_isdeleted == null ? true : Boolean.Parse(_isdeleted));
site.LogoFileId = null;
var logofileid = _logofilemanager.GetFileId();
if (logofileid != -1)
{
site.LogoFileId = logofileid;
}
var faviconFieldId = _faviconfilemanager.GetFileId();
if (faviconFieldId != -1)
{
site.FaviconFileId = faviconFieldId;
}
site.DefaultThemeType = _themetype;
site.DefaultContainerType = _containertype;
site.AdminContainerType = _admincontainertype;
site.PwaIsEnabled = (_pwaisenabled == null ? true : Boolean.Parse(_pwaisenabled));
var pwaappiconfileid = _pwaappiconfilemanager.GetFileId();
if (pwaappiconfileid != -1)
{ {
site.PwaAppIconFileId = pwaappiconfileid; site.PwaAppIconFileId = pwaappiconfileid;
reload = true; // needs to be reloaded on server
} }
var pwasplashiconfileid = _pwasplashiconfilemanager.GetFileId(); int? pwasplashiconfileid = _pwasplashiconfilemanager.GetFileId();
if (pwasplashiconfileid != -1) if (pwasplashiconfileid == -1) pwasplashiconfileid = null;
if (site.PwaSplashIconFileId != pwasplashiconfileid)
{ {
site.PwaSplashIconFileId = pwasplashiconfileid; site.PwaSplashIconFileId = pwasplashiconfileid;
reload = true; // needs to be reloaded on server
} }
site = await SiteService.UpdateSiteAsync(site); site = await SiteService.UpdateSiteAsync(site);
@ -485,7 +512,7 @@
if (refresh) if (refresh)
{ {
NavigationManager.NavigateTo(NavigateUrl()); // refresh to show new theme or container NavigationManager.NavigateTo(NavigateUrl(true), reload); // refresh/reload
} }
else else
{ {

View File

@ -82,6 +82,24 @@ else
</select> </select>
</div> </div>
</div> </div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="runtime" HelpText="The runtime hosting model" ResourceKey="Runtime">Runtime: </Label>
<div class="col-sm-9">
<select id="runtime" class="form-select" @bind="@_runtime" required>
<option value="Server">@SharedLocalizer["BlazorServer"]</option>
<option value="WebAssembly">@SharedLocalizer["BlazorWebAssembly"]</option>
</select>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="prerender" HelpText="Specifies if the site should be prerendered (for search crawlers, etc...)" ResourceKey="Prerender">Prerender? </Label>
<div class="col-sm-9">
<select id="prerender" class="form-select" @bind="@_prerender" required>
<option value="Prerendered">@SharedLocalizer["Yes"]</option>
<option value="">@SharedLocalizer["No"]</option>
</select>
</div>
</div>
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="tenant" HelpText="Select the tenant for the site" ResourceKey="Tenant">Tenant: </Label> <Label Class="col-sm-3" For="tenant" HelpText="Select the tenant for the site" ResourceKey="Tenant">Tenant: </Label>
<div class="col-sm-9"> <div class="col-sm-9">
@ -129,13 +147,13 @@ else
@DatabaseConfigComponent; @DatabaseConfigComponent;
} }
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="hostUsername" HelpText="Enter the username of the host for this site" ResourceKey="HostUsername">Host Username:</Label> <Label Class="col-sm-3" For="hostUsername" HelpText="Enter the username of an existing host user" ResourceKey="HostUsername">Host Username:</Label>
<div class="col-sm-9"> <div class="col-sm-9">
<input id="hostUsername" class="form-control" @bind="@_hostUserName" readonly /> <input id="hostUsername" class="form-control" @bind="@_hostusername" required />
</div> </div>
</div> </div>
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="hostPassword" HelpText="Enter the password for the host of this site" ResourceKey="HostPassword">Host Password:</Label> <Label Class="col-sm-3" For="hostPassword" HelpText="Enter the password of an existing host user" ResourceKey="HostPassword">Host Password:</Label>
<div class="col-sm-9"> <div class="col-sm-9">
<input id="hostPassword" type="password" class="form-control" @bind="@_hostpassword" required /> <input id="hostPassword" type="password" class="form-control" @bind="@_hostpassword" required />
</div> </div>
@ -168,7 +186,7 @@ else
private string _tenantName = string.Empty; private string _tenantName = string.Empty;
private string _hostUserName = UserNames.Host; private string _hostusername = string.Empty;
private string _hostpassword = string.Empty; private string _hostpassword = string.Empty;
private string _name = string.Empty; private string _name = string.Empty;
@ -177,6 +195,8 @@ else
private string _containertype = "-"; private string _containertype = "-";
private string _admincontainertype = ""; private string _admincontainertype = "";
private string _sitetemplatetype = "-"; private string _sitetemplatetype = "-";
private string _runtime = "Server";
private string _prerender = "Prerendered";
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
@ -283,7 +303,7 @@ else
// validate host credentials // validate host credentials
var user = new User(); var user = new User();
user.SiteId = PageState.Site.SiteId; user.SiteId = PageState.Site.SiteId;
user.Username = UserNames.Host; user.Username = _hostusername;
user.Password = _hostpassword; user.Password = _hostpassword;
user = await UserService.LoginUserAsync(user, false, false); user = await UserService.LoginUserAsync(user, false, false);
if (user.IsAuthenticated) if (user.IsAuthenticated)
@ -300,8 +320,9 @@ else
config.TenantName = _tenantName; config.TenantName = _tenantName;
config.DatabaseType = database.DBType; config.DatabaseType = database.DBType;
config.ConnectionString = connectionString; config.ConnectionString = connectionString;
config.HostEmail = user.Email; config.HostUsername = _hostusername;
config.HostPassword = _hostpassword; config.HostPassword = _hostpassword;
config.HostEmail = user.Email;
config.HostName = user.DisplayName; config.HostName = user.DisplayName;
config.IsNewTenant = true; config.IsNewTenant = true;
} }
@ -340,6 +361,8 @@ else
config.DefaultContainer = _containertype; config.DefaultContainer = _containertype;
config.DefaultAdminContainer = _admincontainertype; config.DefaultAdminContainer = _admincontainertype;
config.SiteTemplate = _sitetemplatetype; config.SiteTemplate = _sitetemplatetype;
config.Runtime = _runtime;
config.RenderMode = _runtime + _prerender;
ShowProgressIndicator(); ShowProgressIndicator();

View File

@ -52,6 +52,11 @@ else
private void Edit(string name) private void Edit(string name)
{ {
if (name.Equals("*"))
{
var uri = new Uri(NavigationManager.Uri);
name = uri.Authority;
}
NavigationManager.NavigateTo(_scheme + name + "/admin/site/?reload"); NavigationManager.NavigateTo(_scheme + name + "/admin/site/?reload");
} }

View File

@ -50,24 +50,6 @@
</TabPanel> </TabPanel>
<TabPanel Name="Options" Heading="Options" ResourceKey="Options"> <TabPanel Name="Options" Heading="Options" ResourceKey="Options">
<div class="container"> <div class="container">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="runtime" HelpText="Blazor Runtime (Server or WebAssembly)" ResourceKey="BlazorRuntime">Blazor Runtime: </Label>
<div class="col-sm-9">
<select id="runtime" class="form-select" @bind="@_runtime">
<option value="Server">@Localizer["Server"]</option>
<option value="WebAssembly">@Localizer["WebAssembly"]</option>
</select>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="rendermode" HelpText="Blazor Server Render Mode" ResourceKey="RenderMode">Render Mode: </Label>
<div class="col-sm-9">
<select id="rendermode" class="form-select" @bind="@_rendermode">
<option value="Server">@Localizer["Server"]</option>
<option value="ServerPrerendered">@Localizer["ServerPrerendered"]</option>
</select>
</div>
</div>
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="detailederrors" HelpText="Specify If Detailed Errors Are Enabled For Blazor. This Option Should Not Not Be Enabled In Production." ResourceKey="DetailedErrors">Detailed Errors? </Label> <Label Class="col-sm-3" For="detailederrors" HelpText="Specify If Detailed Errors Are Enabled For Blazor. This Option Should Not Not Be Enabled In Production." ResourceKey="DetailedErrors">Detailed Errors? </Label>
<div class="col-sm-9"> <div class="col-sm-9">
@ -126,8 +108,6 @@
private string _servertime = string.Empty; private string _servertime = string.Empty;
private string _installationid = string.Empty; private string _installationid = string.Empty;
private string _runtime = string.Empty;
private string _rendermode = string.Empty;
private string _detailederrors = string.Empty; private string _detailederrors = string.Empty;
private string _logginglevel = string.Empty; private string _logginglevel = string.Empty;
private string _swagger = string.Empty; private string _swagger = string.Empty;
@ -146,8 +126,6 @@
_servertime = systeminfo["servertime"]; _servertime = systeminfo["servertime"];
_installationid = systeminfo["installationid"]; _installationid = systeminfo["installationid"];
_runtime = systeminfo["runtime"];
_rendermode = systeminfo["rendermode"];
_detailederrors = systeminfo["detailederrors"]; _detailederrors = systeminfo["detailederrors"];
_logginglevel = systeminfo["logginglevel"]; _logginglevel = systeminfo["logginglevel"];
_swagger = systeminfo["swagger"]; _swagger = systeminfo["swagger"];
@ -160,8 +138,6 @@
try try
{ {
var settings = new Dictionary<string, string>(); var settings = new Dictionary<string, string>();
settings.Add("runtime", _runtime);
settings.Add("rendermode", _rendermode);
settings.Add("detailederrors", _detailederrors); settings.Add("detailederrors", _detailederrors);
settings.Add("logginglevel", _logginglevel); settings.Add("logginglevel", _logginglevel);
settings.Add("swagger", _swagger); settings.Add("swagger", _swagger);

View File

@ -70,8 +70,7 @@
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" HelpText="Upload one or more theme packages. Once they are uploaded click Install to complete the installation." ResourceKey="Theme">Theme: </Label> <Label Class="col-sm-3" HelpText="Upload one or more theme packages. Once they are uploaded click Install to complete the installation." ResourceKey="Theme">Theme: </Label>
<div class="col-sm-9"> <div class="col-sm-9">
<FileManager Filter="nupkg" ShowFiles="false" Folder="Packages" UploadMultiple="@true" /> <FileManager Folder="@Constants.PackagesFolder" UploadMultiple="true" />
</div> </div>
</div> </div>
</div> </div>
@ -230,7 +229,7 @@
{ {
try try
{ {
await PackageService.DownloadPackageAsync(_packageid, _version, "Packages"); await PackageService.DownloadPackageAsync(_packageid, _version, Constants.PackagesFolder);
await logger.LogInformation("Package {PackageId} {Version} Downloaded Successfully", _packageid, _version); await logger.LogInformation("Package {PackageId} {Version} Downloaded Successfully", _packageid, _version);
AddModuleMessage(Localizer["Success.Theme.Download"], MessageType.Success); AddModuleMessage(Localizer["Success.Theme.Download"], MessageType.Success);
_productname = ""; _productname = "";

View File

@ -11,55 +11,56 @@
@if (_templates != null) @if (_templates != null)
{ {
<div class="container"> <div class="container">
<div class="row mb-1 align-items-center"> <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 theme. It should not contain spaces or punctuation." ResourceKey="OwnerName">Owner Name: </Label> <Label Class="col-sm-3" For="owner" HelpText="Enter the name of the organization who is developing this theme. It should not contain spaces or punctuation." ResourceKey="OwnerName">Owner Name: </Label>
<div class="col-sm-9"> <div class="col-sm-9">
<input id="owner" class="form-control" @bind="@_owner" /> <input id="owner" class="form-control" @bind="@_owner" />
</div> </div>
</div> </div>
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="module" HelpText="Enter a name for this theme. It should not contain spaces or punctuation." ResourceKey="ThemeName">Theme Name: </Label> <Label Class="col-sm-3" For="module" HelpText="Enter a name for this theme. It should not contain spaces or punctuation." ResourceKey="ThemeName">Theme Name: </Label>
<div class="col-sm-9"> <div class="col-sm-9">
<input id="module" class="form-control" @bind="@_theme" /> <input id="module" class="form-control" @bind="@_theme" />
</div> </div>
</div> </div>
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="template" HelpText="Select a theme template. Templates are located in the wwwroot/Themes/Templates folder on the server." ResourceKey="Template">Template: </Label> <Label Class="col-sm-3" For="template" HelpText="Select a theme template. Templates are located in the wwwroot/Themes/Templates folder on the server." ResourceKey="Template">Template: </Label>
<div class="col-sm-9"> <div class="col-sm-9">
<select id="template" class="form-select" @onchange="(e => TemplateChanged(e))"> <select id="template" class="form-select" @onchange="(e => TemplateChanged(e))">
<option value="-">&lt;@Localizer["Template.Select"]&gt;</option> <option value="-">&lt;@Localizer["Template.Select"]&gt;</option>
@foreach (Template template in _templates) @foreach (Template template in _templates)
{ {
<option value="@template.Name">@template.Title</option> <option value="@template.Name">@template.Title</option>
} }
</select> </select>
</div> </div>
</div> </div>
<div class="row mb-1 align-items-center"> <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> <Label Class="col-sm-3" For="reference" HelpText="Select a framework reference version" ResourceKey="FrameworkReference">Framework Reference: </Label>
<div class="col-sm-9"> <div class="col-sm-9">
<select id="reference" class="form-select" @bind="@_reference"> <select id="reference" class="form-select" @bind="@_reference">
@foreach (string version in _versions) @foreach (string version in _versions)
{ {
if (Version.Parse(version).CompareTo(Version.Parse(_minversion)) >= 0) if (Version.Parse(version).CompareTo(Version.Parse(_minversion)) >= 0)
{ {
<option value="@(version)">@(version)</option> <option value="@(version)">@(version)</option>
} }
} }
<option value="local">@SharedLocalizer["LocalVersion"]</option> <option value="local">@SharedLocalizer["LocalVersion"]</option>
</select> </select>
</div> </div>
</div> </div>
@if (!string.IsNullOrEmpty(_location)) { @if (!string.IsNullOrEmpty(_location))
<div class="row mb-1 align-items-center"> {
<Label Class="col-sm-3" For="location" HelpText="Location where the theme will be created" ResourceKey="Location">Location: </Label> <div class="row mb-1 align-items-center">
<div class="col-sm-9"> <Label Class="col-sm-3" For="location" HelpText="Location where the theme will be created" ResourceKey="Location">Location: </Label>
<input id="module" class="form-control" @bind="@_location" readonly /> <div class="col-sm-9">
</div> <input id="module" class="form-control" @bind="@_location" readonly />
</div> </div>
} </div>
</div> }
</div>
<br /> <br />
<button type="button" class="btn btn-success" @onclick="CreateTheme">@Localizer["Theme.Create"]</button> <button type="button" class="btn btn-success" @onclick="CreateTheme">@Localizer["Theme.Create"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink> <NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
@ -77,13 +78,17 @@
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
protected override async Task OnParametersSetAsync() protected override void OnInitialized()
{
AddModuleMessage(Localizer["Info.Theme.CreatorIntent"], MessageType.Info);
}
protected override async Task OnParametersSetAsync()
{ {
try try
{ {
_templates = await ThemeService.GetThemeTemplatesAsync(); _templates = await ThemeService.GetThemeTemplatesAsync();
_versions = Constants.ReleaseVersions.Split(',').Where(item => Version.Parse(item).CompareTo(Version.Parse("2.0.0")) >= 0).ToArray(); _versions = Constants.ReleaseVersions.Split(',').Where(item => Version.Parse(item).CompareTo(Version.Parse("2.0.0")) >= 0).ToArray();
AddModuleMessage(Localizer["Info.Theme.CreatorIntent"], MessageType.Info);
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@ -112,7 +112,7 @@ else
{ {
try try
{ {
await PackageService.DownloadPackageAsync(packagename, version, "Packages"); await PackageService.DownloadPackageAsync(packagename, version, Constants.PackagesFolder);
await logger.LogInformation("Theme Downloaded {ThemeName} {Version}", packagename, version); await logger.LogInformation("Theme Downloaded {ThemeName} {Version}", packagename, version);
await ThemeService.InstallThemesAsync(); await ThemeService.InstallThemesAsync();
AddModuleMessage(string.Format(Localizer["Success.Theme.Install"], NavigateUrl("admin/system")), MessageType.Success); AddModuleMessage(string.Format(Localizer["Success.Theme.Install"], NavigateUrl("admin/system")), MessageType.Success);

View File

@ -26,7 +26,7 @@
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" HelpText="Upload A Framework Package And Then Select Upgrade" ResourceKey="Framework">Framework: </Label> <Label Class="col-sm-3" HelpText="Upload A Framework Package And Then Select Upgrade" ResourceKey="Framework">Framework: </Label>
<div class="col-sm-9"> <div class="col-sm-9">
<FileManager Filter="nupkg" ShowFiles="false" Folder="Packages" /> <FileManager Folder="@Constants.PackagesFolder" />
</div> </div>
</div> </div>
</div> </div>
@ -85,8 +85,8 @@
{ {
try try
{ {
await PackageService.DownloadPackageAsync(packageid, version, "Packages"); await PackageService.DownloadPackageAsync(packageid, version, Constants.PackagesFolder);
await PackageService.DownloadPackageAsync(Constants.UpdaterPackageId, version, "Packages"); await PackageService.DownloadPackageAsync(Constants.UpdaterPackageId, version, Constants.PackagesFolder);
AddModuleMessage(Localizer["Success.Framework.Download"], MessageType.Success); AddModuleMessage(Localizer["Success.Framework.Download"], MessageType.Success);
} }
catch (Exception ex) catch (Exception ex)

View File

@ -0,0 +1,71 @@
@namespace Oqtane.Modules.Admin.UrlMappings
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IUrlMappingService UrlMappingService
@inject IStringLocalizer<Edit> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer
<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="url" HelpText="The fully qualified Url for this site" ResourceKey="Url">Url:</Label>
<div class="col-sm-9">
<input id="url" class="form-control" @bind="@_url" maxlength="500" required />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="mappedurl" HelpText="A fully qualified Url where the user will be redirected" ResourceKey="MappedUrl">Redirect To:</Label>
<div class="col-sm-9">
<input id="mappedurl" class="form-control" @bind="@_mappedurl" maxlength="500" required />
</div>
</div>
<br /><br />
<button type="button" class="btn btn-success" @onclick="SaveUrlMapping">@SharedLocalizer["Save"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
</div>
</form>
@code {
private ElementReference form;
private bool validated = false;
private string _url = string.Empty;
private string _mappedurl = string.Empty;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
private async Task SaveUrlMapping()
{
validated = true;
var interop = new Interop(JSRuntime);
if (await interop.FormValid(form))
{
var route = new Route(_url, PageState.Alias.Path);
var url = route.SiteUrl + "/" + route.PagePath;
var urlmapping = new UrlMapping();
urlmapping.SiteId = PageState.Site.SiteId;
urlmapping.Url = url;
urlmapping.MappedUrl = _mappedurl;
urlmapping.Requests = 0;
urlmapping.CreatedOn = DateTime.UtcNow;
urlmapping.RequestedOn = DateTime.UtcNow;
try
{
urlmapping = await UrlMappingService.AddUrlMappingAsync(urlmapping);
await logger.LogInformation("UrlMapping Saved {UrlMapping}", urlmapping);
NavigationManager.NavigateTo(NavigateUrl());
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Saving UrlMapping {UrlMapping} {Error}", urlmapping, ex.Message);
AddModuleMessage(Localizer["Error.SaveUrlMapping"], MessageType.Error);
}
}
else
{
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
}
}
}

View File

@ -0,0 +1,83 @@
@namespace Oqtane.Modules.Admin.UrlMappings
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IUrlMappingService UrlMappingService
@inject IStringLocalizer<Edit> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer
<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="url" HelpText="A fully qualified Url for this site" ResourceKey="Url">Url:</Label>
<div class="col-sm-9">
<input id="url" class="form-control" @bind="@_url" maxlength="500" readonly />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="mappedurl" HelpText="A fully qualified Url where the user will be redirected" ResourceKey="MappedUrl">Redirect To:</Label>
<div class="col-sm-9">
<input id="mappedurl" class="form-control" @bind="@_mappedurl" maxlength="500" required />
</div>
</div>
<br /><br />
<button type="button" class="btn btn-success" @onclick="SaveUrlMapping">@SharedLocalizer["Save"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
</div>
</form>
@code {
private ElementReference form;
private bool validated = false;
private int _urlmappingid;
private string _url = string.Empty;
private string _mappedurl = string.Empty;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
protected override async Task OnInitializedAsync()
{
try
{
_urlmappingid = Int32.Parse(PageState.QueryString["id"]);
var urlmapping = await UrlMappingService.GetUrlMappingAsync(_urlmappingid);
if (urlmapping != null)
{
_url = urlmapping.Url;
_mappedurl = urlmapping.MappedUrl;
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading UrlMapping {UrlMappingId} {Error}", _urlmappingid, ex.Message);
AddModuleMessage(Localizer["Error.LoadUrlMapping"], MessageType.Error);
}
}
private async Task SaveUrlMapping()
{
validated = true;
var interop = new Interop(JSRuntime);
if (await interop.FormValid(form))
{
var urlmapping = await UrlMappingService.GetUrlMappingAsync(_urlmappingid);
urlmapping.MappedUrl = _mappedurl;
try
{
urlmapping = await UrlMappingService.UpdateUrlMappingAsync(urlmapping);
await logger.LogInformation("UrlMapping Saved {UrlMapping}", urlmapping);
NavigationManager.NavigateTo(NavigateUrl());
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Saving UrlMapping {UrlMapping} {Error}", urlmapping, ex.Message);
AddModuleMessage(Localizer["Error.SaveUrlMapping"], MessageType.Error);
}
}
else
{
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
}
}
}

View File

@ -0,0 +1,140 @@
@namespace Oqtane.Modules.Admin.UrlMappings
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IUrlMappingService UrlMappingService
@inject ISiteService SiteService
@inject IStringLocalizer<Index> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer
@if (_urlMappings == null)
{
<p><em>@SharedLocalizer["Loading"]</em></p>
}
else
{
<TabStrip>
<TabPanel Name="Urls" Heading="Urls" ResourceKey="Urls">
<div class="container">
<div class="row mb-1 align-items-center">
<div class="col-sm-6">
<ActionLink Action="Add" Text="Add Url Mapping" ResourceKey="AddUrlMapping" />
</div>
<div class="col-sm-6">
<select id="type" class="form-select custom-select" @onchange="(e => MappedChanged(e))">
<option value="true">@Localizer["Mapped"]</option>
<option value="false">@Localizer["Broken"]</option>
</select>
</div>
</div>
</div>
<br/>
<Pager Items="@_urlMappings">
<Header>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>@Localizer["Url"]</th>
<th>@Localizer["Requests"]</th>
<th>@Localizer["Requested"]</th>
</Header>
<Row>
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.UrlMappingId.ToString())" ResourceKey="Edit" /></td>
<td><ActionDialog Header="Delete Url Mapping" Message="@string.Format(Localizer["Confirm.DeleteUrlMapping"], context.Url)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteUrlMapping(context))" ResourceKey="DeleteUrlMapping" /></td>
<td>
<a href="" onclick="@(() => BrowseUrl(context.Url))">@context.Url</a>
@if (_mapped)
{
@((MarkupString)"<br />&gt;&gt;&nbsp;")<a href="" onclick="@(() => BrowseUrl(context.MappedUrl))">@context.MappedUrl</a>
}
</td>
<td>@context.Requests</td>
<td>@context.RequestedOn</td>
</Row>
</Pager>
</TabPanel>
<TabPanel Name="Settings" Heading="Settings" ResourceKey="Settings">
<div class="container">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="capturebrokenurls" HelpText="Specify if broken Urls should be captured automatically and saved in Url Mappings" ResourceKey="CaptureBrokenUrls">Capture Broken Urls? </Label>
<div class="col-sm-9">
<select id="capturebrokenurls" class="form-select" @bind="@_capturebrokenurls" >
<option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option>
</select>
</div>
</div>
</div>
<br />
<button type="button" class="btn btn-success" @onclick="SaveSiteSettings">@SharedLocalizer["Save"]</button>
</TabPanel>
</TabStrip>
}
@code {
private bool _mapped = true;
private List<UrlMapping> _urlMappings;
private string _capturebrokenurls;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
protected override async Task OnParametersSetAsync()
{
await GetUrlMappings();
_capturebrokenurls = PageState.Site.CaptureBrokenUrls.ToString();
}
private async void MappedChanged(ChangeEventArgs e)
{
try
{
_mapped = bool.Parse(e.Value.ToString());
await GetUrlMappings();
StateHasChanged();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error On TypeChanged");
}
}
private void BrowseUrl(string url)
{
NavigationManager.NavigateTo(url, true);
}
private async Task DeleteUrlMapping(UrlMapping urlMapping)
{
try
{
await UrlMappingService.DeleteUrlMappingAsync(urlMapping.UrlMappingId);
await logger.LogInformation("UrlMapping Deleted {UrlMapping}", urlMapping);
await GetUrlMappings();
StateHasChanged();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Deleting UrlMapping {UrlMapping} {Error}", urlMapping, ex.Message);
AddModuleMessage(Localizer["Error.DeleteUrlMapping"], MessageType.Error);
}
}
private async Task GetUrlMappings()
{
_urlMappings = await UrlMappingService.GetUrlMappingsAsync(PageState.Site.SiteId, _mapped);
}
private async Task SaveSiteSettings()
{
try
{
var site = PageState.Site;
site.CaptureBrokenUrls = bool.Parse(_capturebrokenurls);
await SiteService.UpdateSiteAsync(site);
AddModuleMessage(Localizer["Success.SaveSiteSettings"], MessageType.Success);
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Saving Site Settings {Error}", ex.Message);
AddModuleMessage(Localizer["Error.SaveSiteSettings"], MessageType.Error);
}
}
}

View File

@ -3,6 +3,7 @@
@inject IUserRoleService UserRoleService @inject IUserRoleService UserRoleService
@inject IUserService UserService @inject IUserService UserService
@inject ISettingService SettingService @inject ISettingService SettingService
@inject ISiteService SiteService
@inject IStringLocalizer<Index> Localizer @inject IStringLocalizer<Index> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer @inject IStringLocalizer<SharedResources> SharedLocalizer
@ -14,45 +15,65 @@
} }
else else
{ {
<div class="container"> <TabStrip>
<div class="row mb-1 align-items-center"> <TabPanel Name="Users" Heading="Users" ResourceKey="Users">
<div class="col-sm-4"> <div class="container">
<ActionLink Action="Add" Text="Add User" ResourceKey="AddUser" /> <div class="row mb-1 align-items-center">
</div> <div class="col-sm-4">
<div class="col-sm-4"> <ActionLink Action="Add" Text="Add User" ResourceKey="AddUser" />
<input class="form-control" @bind="@_search" /> </div>
</div> <div class="col-sm-4">
<div class="col-sm-4"> <input class="form-control" @bind="@_search" />
<button class="btn btn-secondary" @onclick="OnSearch">@SharedLocalizer["Search"]</button> </div>
</div> <div class="col-sm-4">
</div> <button type="button" class="btn btn-secondary" @onclick="OnSearch">@SharedLocalizer["Search"]</button>
</div> </div>
<Pager Items="@userroles"> </div>
<Header> </div>
<th style="width: 1px;">&nbsp;</th> <Pager Items="@userroles">
<th style="width: 1px;">&nbsp;</th> <Header>
<th style="width: 1px;">&nbsp;</th> <th style="width: 1px;">&nbsp;</th>
<th>@SharedLocalizer["Name"]</th> <th style="width: 1px;">&nbsp;</th>
</Header> <th style="width: 1px;">&nbsp;</th>
<Row> <th>@SharedLocalizer["Name"]</th>
<td> </Header>
<ActionLink Action="Edit" Parameters="@($"id=" + context.UserId.ToString())" ResourceKey="EditUser" /> <Row>
</td> <td>
<td> <ActionLink Action="Edit" Parameters="@($"id=" + context.UserId.ToString())" ResourceKey="EditUser" />
<ActionDialog Header="Delete User" Message="@string.Format(Localizer["Confirm.User.Delete"], context.User.DisplayName)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteUser(context))" Disabled="@(context.UserId == PageState.User.UserId)" ResourceKey="DeleteUser" /> </td>
</td> <td>
<td> <ActionDialog Header="Delete User" Message="@string.Format(Localizer["Confirm.User.Delete"], context.User.DisplayName)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteUser(context))" Disabled="@(context.UserId == PageState.User.UserId)" ResourceKey="DeleteUser" />
<ActionLink Action="Roles" Parameters="@($"id=" + context.UserId.ToString())" ResourceKey="Roles" /> </td>
</td> <td>
<td>@context.User.DisplayName</td> <ActionLink Action="Roles" Parameters="@($"id=" + context.UserId.ToString())" ResourceKey="Roles" />
</Row> </td>
</Pager> <td>@context.User.DisplayName</td>
</Row>
</Pager>
</TabPanel>
<TabPanel Name="Settings" Heading="Settings" ResourceKey="Settings">
<div class="container">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="allowregistration" HelpText="Do you want to allow visitors to be able to register for a user account on the site" ResourceKey="AllowRegistration">Allow User Registration? </Label>
<div class="col-sm-9">
<select id="allowregistration" class="form-select" @bind="@_allowregistration" required>
<option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option>
</select>
</div>
</div>
</div>
<br />
<button type="button" class="btn btn-success" @onclick="SaveSiteSettings">@SharedLocalizer["Save"]</button>
</TabPanel>
</TabStrip>
} }
@code { @code {
private List<UserRole> allroles; private List<UserRole> allroles;
private List<UserRole> userroles; private List<UserRole> userroles;
private string _search; private string _search;
private string _allowregistration;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
@ -61,6 +82,7 @@ else
allroles = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId); allroles = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId);
await LoadSettingsAsync(); await LoadSettingsAsync();
userroles = Search(_search); userroles = Search(_search);
_allowregistration = PageState.Site.AllowRegistration.ToString();
} }
private List<UserRole> Search(string search) private List<UserRole> Search(string search)
@ -122,4 +144,20 @@ else
await SettingService.UpdateUserSettingsAsync(settings, PageState.User.UserId); await SettingService.UpdateUserSettingsAsync(settings, PageState.User.UserId);
} }
private async Task SaveSiteSettings()
{
try
{
var site = PageState.Site;
site.AllowRegistration = bool.Parse(_allowregistration);
await SiteService.UpdateSiteAsync(site);
AddModuleMessage(Localizer["Success.SaveSiteSettings"], MessageType.Success);
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Saving Site Settings {Error}", ex.Message);
AddModuleMessage(Localizer["Error.SaveSiteSettings"], MessageType.Error);
}
}
} }

View File

@ -0,0 +1,123 @@
@namespace Oqtane.Modules.Admin.Visitors
@using System.Globalization
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IVisitorService VisitorService
@inject IUserService UserService
@inject IStringLocalizer<Detail> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer
<div class="container">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="ip" HelpText="The last recorded IP address for this visitor" ResourceKey="IP">IP Address: </Label>
<div class="col-sm-9">
<input id="ip" class="form-control" @bind="@_ip" readonly />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="language" HelpText="The last recorded language for this visitor" ResourceKey="Language">Language: </Label>
<div class="col-sm-9">
<input id="language" class="form-control" @bind="@_language" readonly />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="useragent" HelpText="The last recorded user agent for this visitor" ResourceKey="UserAgent">User Agent: </Label>
<div class="col-sm-9">
<input id="useragent" class="form-control" @bind="@_useragent" readonly />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="url" HelpText="The last recorded url for this visitor" ResourceKey="Url">Url: </Label>
<div class="col-sm-9">
<input id="url" class="form-control" @bind="@_url" readonly />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="referrer" HelpText="The last recorded referrer for this visitor" ResourceKey="Referrer">Referrer: </Label>
<div class="col-sm-9">
<input id="referrer" class="form-control" @bind="@_referrer" readonly />
</div>
</div>
@if (_user != string.Empty)
{
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="user" HelpText="The last recorded user associated with this visitor" ResourceKey="User">User: </Label>
<div class="col-sm-9">
<input id="user" class="form-control" @bind="@_user" readonly />
</div>
</div>
}
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="visits" HelpText="The total number of visits by this visitor all time" ResourceKey="Visits">Visits: </Label>
<div class="col-sm-9">
<input id="visits" class="form-control" @bind="@_visits" readonly />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="visited" HelpText="The last recorded date/time when the visitor visited the site" ResourceKey="Visited">Visited: </Label>
<div class="col-sm-9">
<input id="visited" class="form-control" @bind="@_visited" readonly />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="created" HelpText="The first recorded date/time when this visitor visited the site" ResourceKey="Created">Created: </Label>
<div class="col-sm-9">
<input id="created" class="form-control" @bind="@_created" readonly />
</div>
</div>
</div>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
@code {
private int _visitorId;
private string _ip = string.Empty;
private string _language = string.Empty;
private string _useragent = string.Empty;
private string _url = string.Empty;
private string _referrer = string.Empty;
private string _user = string.Empty;
private string _visits = string.Empty;
private string _visited = string.Empty;
private string _created = string.Empty;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
protected override async Task OnInitializedAsync()
{
try
{
_visitorId = Int32.Parse(PageState.QueryString["id"]);
var visitor = await VisitorService.GetVisitorAsync(_visitorId);
if (visitor != null)
{
_ip = visitor.IPAddress;
_language = visitor.Language;
_useragent = visitor.UserAgent;
_url = visitor.Url;
_referrer = visitor.Referrer;
_visits = visitor.Visits.ToString();
_visited = visitor.VisitedOn.ToString(CultureInfo.CurrentCulture);
_created = visitor.CreatedOn.ToString(CultureInfo.CurrentCulture);
if (visitor.UserId != null)
{
var user = await UserService.GetUserAsync(visitor.UserId.Value, PageState.Site.SiteId);
if (user != null)
{
_user = user.DisplayName;
}
}
}
else
{
AddModuleMessage(Localizer["Error.LoadVisitor"], MessageType.Error);
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading Visitor {VisitorId} {Error}", _visitorId, ex.Message);
AddModuleMessage(Localizer["Error.LoadVisitor"], MessageType.Error);
}
}
}

View File

@ -0,0 +1,144 @@
@namespace Oqtane.Modules.Admin.Visitors
@inherits ModuleBase
@inject IVisitorService VisitorService
@inject ISiteService SiteService
@inject IStringLocalizer<Index> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer
@if (_visitors == null)
{
<p><em>@SharedLocalizer["Loading"]</em></p>
}
else
{
<TabStrip>
<TabPanel Name="Visitors" Heading="Visitors" ResourceKey="Visitors">
<div class="container">
<div class="row mb-1 align-items-center">
<div class="col-sm-6">
<select id="type" class="form-select custom-select" @onchange="(e => TypeChanged(e))">
<option value="false">@Localizer["AllVisitors"]</option>
<option value="true">@Localizer["UsersOnly"]</option>
</select>
</div>
<div class="col-sm-6">
<select id="type" class="form-select custom-select" @onchange="(e => DateChanged(e))">
<option value="1">@Localizer["PastDay"]</option>
<option value="7">@Localizer["PastWeek"]</option>
<option value="30">@Localizer["PastMonth"]</option>
</select>
</div>
</div>
</div>
<br/>
<Pager Items="@_visitors">
<Header>
<th style="width: 1px;">&nbsp;</th>
<th>@Localizer["IP"]</th>
<th>@Localizer["User"]</th>
<th>@Localizer["Language"]</th>
<th>@Localizer["Visits"]</th>
<th>@Localizer["Visited"]</th>
<th>@Localizer["Created"]</th>
</Header>
<Row>
<td><ActionLink Action="Detail" Parameters="@($"id=" + context.VisitorId.ToString())" ResourceKey="Details" /></td>
<td>@context.IPAddress</td>
<td>
@if (context.UserId != null)
{
@context.User.DisplayName
}
</td>
<td>@context.Language</td>
<td>@context.Visits</td>
<td>@context.VisitedOn</td>
<td>@context.CreatedOn</td>
</Row>
</Pager>
</TabPanel>
<TabPanel Name="Settings" Heading="Settings" ResourceKey="Settings">
<div class="container">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="visitortracking" HelpText="Specify if visitor tracking is enabled" ResourceKey="VisitorTracking">Visitor Tracking Enabled? </Label>
<div class="col-sm-9">
<select id="visitortracking" class="form-select" @bind="@_visitortracking" >
<option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option>
</select>
</div>
</div>
</div>
<br />
<button type="button" class="btn btn-success" @onclick="SaveSiteSettings">@SharedLocalizer["Save"]</button>
</TabPanel>
</TabStrip>
}
@code {
private bool _users = false;
private int _days = 1;
private List<Visitor> _visitors;
private string _visitortracking;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
protected override async Task OnParametersSetAsync()
{
await GetVisitors();
_visitortracking = PageState.Site.VisitorTracking.ToString();
}
private async void TypeChanged(ChangeEventArgs e)
{
try
{
_users = bool.Parse(e.Value.ToString());
await GetVisitors();
StateHasChanged();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error On TypeChanged");
}
}
private async void DateChanged(ChangeEventArgs e)
{
try
{
_days = int.Parse(e.Value.ToString());
await GetVisitors();
StateHasChanged();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error On DateChanged");
}
}
private async Task GetVisitors()
{
_visitors = await VisitorService.GetVisitorsAsync(PageState.Site.SiteId, DateTime.UtcNow.AddDays(-_days));
if (_users)
{
_visitors = _visitors.Where(item => item.UserId != null).ToList();
}
}
private async Task SaveSiteSettings()
{
try
{
var site = PageState.Site;
site.VisitorTracking = bool.Parse(_visitortracking);
await SiteService.UpdateSiteAsync(site);
AddModuleMessage(Localizer["Success.SaveSiteSettings"], MessageType.Success);
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Saving Site Settings {Error}", ex.Message);
AddModuleMessage(Localizer["Error.SaveSiteSettings"], MessageType.Error);
}
}
}

View File

@ -17,7 +17,7 @@
<div class="modal-footer"> <div class="modal-footer">
@if (!string.IsNullOrEmpty(Action)) @if (!string.IsNullOrEmpty(Action))
{ {
<button type="button" class="@Class" @onclick="Confirm">@((MarkupString)_iconSpan) @Localize(Action)</button> <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">@Localize("Cancel")</button>
</div> </div>
@ -30,16 +30,17 @@
{ {
if (Disabled) if (Disabled)
{ {
<button class="@Class" disabled>@((MarkupString)_iconSpan) @Text</button> <button type="button" class="@Class" disabled>@((MarkupString)_iconSpan) @Text</button>
} }
else else
{ {
<button class="@Class" @onclick="DisplayModal">@((MarkupString)_iconSpan) @Text</button> <button type="button" class="@Class" @onclick="DisplayModal">@((MarkupString)_iconSpan) @Text</button>
} }
} }
@code { @code {
private bool _visible = false; private bool _visible = false;
private string _permissions = string.Empty;
private bool _editmode = false; private bool _editmode = false;
private bool _authorized = false; private bool _authorized = false;
private string _iconSpan = string.Empty; private string _iconSpan = string.Empty;
@ -59,6 +60,9 @@
[Parameter] [Parameter]
public SecurityAccessLevel? Security { get; set; } // optional - can be used to explicitly specify SecurityAccessLevel public SecurityAccessLevel? Security { get; set; } // optional - can be used to explicitly specify SecurityAccessLevel
[Parameter]
public string Permissions { get; set; } // optional - can be used to specify a permission string
[Parameter] [Parameter]
public string Class { get; set; } // optional public string Class { get; set; } // optional
@ -105,6 +109,7 @@
Header = Localize(nameof(Header), Header); Header = Localize(nameof(Header), Header);
Message = Localize(nameof(Message), Message); Message = Localize(nameof(Message), Message);
_permissions = (string.IsNullOrEmpty(Permissions)) ? ModuleState.Permissions : Permissions;
_authorized = IsAuthorized(); _authorized = IsAuthorized();
} }
@ -138,10 +143,10 @@
authorized = true; authorized = true;
break; break;
case SecurityAccessLevel.View: case SecurityAccessLevel.View:
authorized = UserSecurity.IsAuthorized(PageState.User,PermissionNames.View, ModuleState.Permissions); authorized = UserSecurity.IsAuthorized(PageState.User,PermissionNames.View, _permissions);
break; break;
case SecurityAccessLevel.Edit: case SecurityAccessLevel.Edit:
authorized = UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, ModuleState.Permissions); authorized = UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, _permissions);
break; break;
case SecurityAccessLevel.Admin: case SecurityAccessLevel.Admin:
authorized = UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin); authorized = UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin);

View File

@ -6,101 +6,118 @@
{ {
if (Disabled) if (Disabled)
{ {
<button class="@_classname" style="@_style" disabled>@((MarkupString)_iconSpan) @_text</button> <button type="button" class="@_classname" style="@_style" disabled>@((MarkupString)_iconSpan) @_text</button>
} }
else else
{ {
<NavLink class="@_classname" href="@_url" style="@_style">@((MarkupString)_iconSpan) @_text</NavLink> if (OnClick == null)
} {
<NavLink class="@_classname" href="@_url" style="@_style">@((MarkupString)_iconSpan) @_text</NavLink>
}
else
{
<button type="button" class="@_classname" style="@_style" onclick="@OnClick">@((MarkupString)_iconSpan) @_text</button>
}
}
} }
@code { @code {
private string _text = string.Empty; private string _text = string.Empty;
private string _url = string.Empty; private string _parameters = string.Empty;
private string _parameters = string.Empty; private string _url = string.Empty;
private string _classname = "btn btn-primary"; private string _permissions = string.Empty;
private string _style = string.Empty; private bool _editmode = false;
private bool _editmode = false; private bool _authorized = false;
private bool _authorized = false; private string _classname = "btn btn-primary";
private string _iconSpan = string.Empty; private string _style = string.Empty;
private string _iconSpan = string.Empty;
[Parameter] [Parameter]
public string Action { get; set; } // required public string Action { get; set; } // required
[Parameter] [Parameter]
public SecurityAccessLevel? Security { get; set; } // optional - can be used to explicitly specify SecurityAccessLevel public string Text { get; set; } // optional - defaults to Action if not specified
[Parameter] [Parameter]
public string Text { get; set; } // optional - defaults to Action if not specified public string Parameters { get; set; } // optional - querystring parameters should be in the form of "id=x&name=y"
[Parameter] [Parameter]
public string Parameters { get; set; } // optional - querystring parameter should be in the form of "id=x&name=y" public int ModuleId { get; set; } = -1; // optional - allows the link to target a specific moduleid
[Parameter] [Parameter]
public string Class { get; set; } // optional - defaults to primary if not specified public Action OnClick { get; set; } = null; // optional - executes a method in the calling component
[Parameter] [Parameter]
public string Style { get; set; } // optional public SecurityAccessLevel? Security { get; set; } // optional - can be used to explicitly specify SecurityAccessLevel
[Parameter] [Parameter]
public bool Disabled { get; set; } // optional public string Permissions { get; set; } // optional - can be used to specify a permission string
[Parameter] [Parameter]
public string EditMode { get; set; } // optional - specifies if an authorized user must be in edit mode to see the action - default is false. public bool Disabled { get; set; } // optional
[Parameter] [Parameter]
public string IconName { get; set; } // optional - specifies an icon for the link - default is no icon public string EditMode { get; set; } // optional - specifies if an authorized user must be in edit mode to see the action - default is false.
[Parameter] [Parameter]
public bool IconOnly { get; set; } // optional - specifies only icon in link public string Class { get; set; } // optional - defaults to primary if not specified
protected override void OnParametersSet() [Parameter]
{ public string Style { get; set; } // optional
base.OnParametersSet();
_text = Action; [Parameter]
if (!string.IsNullOrEmpty(Text)) public string IconName { get; set; } // optional - specifies an icon for the link - default is no icon
{
_text = Text;
}
if (IconOnly && !string.IsNullOrEmpty(IconName)) [Parameter]
{ public bool IconOnly { get; set; } // optional - specifies only icon in link
_text = string.Empty;
}
if (!string.IsNullOrEmpty(Parameters)) protected override void OnParametersSet()
{ {
_parameters = Parameters; base.OnParametersSet();
}
if (!string.IsNullOrEmpty(Class)) _text = Action;
{ if (!string.IsNullOrEmpty(Text))
_classname = Class; {
} _text = Text;
}
if (!string.IsNullOrEmpty(Style)) if (IconOnly && !string.IsNullOrEmpty(IconName))
{ {
_style = Style; _text = string.Empty;
} }
if (!string.IsNullOrEmpty(EditMode)) if (!string.IsNullOrEmpty(Parameters))
{ {
_editmode = bool.Parse(EditMode); _parameters = Parameters;
} }
if (!string.IsNullOrEmpty(IconName)) if (!string.IsNullOrEmpty(Class))
{ {
if (!IconName.Contains(" ")) _classname = Class;
{ }
IconName = "oi oi-" + IconName;
}
_iconSpan = $"<span class=\"{IconName}\"></span>{(IconOnly ? "" : "&nbsp")}";
} if (!string.IsNullOrEmpty(Style))
{
_style = Style;
}
_text = Localize(nameof(Text), _text); if (!string.IsNullOrEmpty(EditMode))
_url = EditUrl(Action, _parameters); {
_editmode = bool.Parse(EditMode);
}
if (!string.IsNullOrEmpty(IconName))
{
if (!IconName.Contains(" "))
{
IconName = "oi oi-" + IconName;
}
_iconSpan = $"<span class=\"{IconName}\"></span>{(IconOnly ? "" : "&nbsp")}";
}
_permissions = (string.IsNullOrEmpty(Permissions)) ? ModuleState.Permissions : Permissions;
_text = Localize(nameof(Text), _text);
_url = (ModuleId == -1) ? EditUrl(Action, _parameters) : EditUrl(ModuleId, Action, _parameters);
_authorized = IsAuthorized(); _authorized = IsAuthorized();
} }
@ -136,10 +153,10 @@
authorized = true; authorized = true;
break; break;
case SecurityAccessLevel.View: case SecurityAccessLevel.View:
authorized = UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, ModuleState.Permissions); authorized = UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, _permissions);
break; break;
case SecurityAccessLevel.Edit: case SecurityAccessLevel.Edit:
authorized = UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, ModuleState.Permissions); authorized = UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, _permissions);
break; break;
case SecurityAccessLevel.Admin: case SecurityAccessLevel.Admin:
authorized = UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin); authorized = UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin);

View File

@ -35,6 +35,9 @@
[Parameter] [Parameter]
public string Style { get; set; } public string Style { get; set; }
[Parameter]
public string DateTimeFormat { get; set; } = "MMM dd yyyy HH:mm:ss";
protected override void OnParametersSet() protected override void OnParametersSet()
{ {
_text = string.Empty; _text = string.Empty;
@ -49,7 +52,7 @@
if (CreatedOn != null) if (CreatedOn != null)
{ {
_text += $" {Localizer["On"]} <b>{CreatedOn.Value.ToString("MMM dd yyyy HH:mm:ss")}</b>"; _text += $" {Localizer["On"]} <b>{CreatedOn.Value.ToString(DateTimeFormat)}</b>";
} }
_text += "</p>"; _text += "</p>";
@ -66,7 +69,7 @@
if (ModifiedOn != null) if (ModifiedOn != null)
{ {
_text += $" {Localizer["on"]} <b>{ModifiedOn.Value.ToString("MMM dd yyyy HH:mm:ss")}</b>"; _text += $" {Localizer["on"]} <b>{ModifiedOn.Value.ToString(DateTimeFormat)}</b>";
} }
_text += "</p>"; _text += "</p>";
@ -83,7 +86,7 @@
if (DeletedOn != null) if (DeletedOn != null)
{ {
_text += $" {Localizer["On"]} <b>{DeletedOn.Value.ToString("MMM dd yyyy HH:mm:ss")}</b>"; _text += $" {Localizer["On"]} <b>{DeletedOn.Value.ToString(DateTimeFormat)}</b>";
} }
_text += "</p>"; _text += "</p>";

View File

@ -10,54 +10,62 @@
<div id="@Id" class="container-fluid px-0"> <div id="@Id" class="container-fluid px-0">
<div class="row"> <div class="row">
<div class="col"> <div class="col">
@if (ShowFolders || FolderId <= 0) <div class="container-fluid px-0">
{ @if (ShowFolders)
<div> {
<select class="form-select" value="@FolderId" @onchange="(e => FolderChanged(e))"> <div class="row">
@if (string.IsNullOrEmpty(Folder)) <div class="col">
{ <select class="form-select" value="@FolderId" @onchange="(e => FolderChanged(e))">
<option value="-1">&lt;@Localizer["Folder.Select"]&gt;</option> <option value="-1">&lt;@Localizer["Folder.Select"]&gt;</option>
} @foreach (Folder folder in _folders)
@foreach (Folder folder in _folders) {
{ <option value="@(folder.FolderId)">@(new string('-', folder.Level * 2))@(folder.Name)</option>
<option value="@(folder.FolderId)">@(new string('-', folder.Level * 2))@(folder.Name)</option> }
} </select>
</select> </div>
</div> </div>
} }
@if (ShowFiles) @if (ShowFiles)
{ {
<div> <div class="row mt-1">
<select class="form-select" value="@FileId" @onchange="(e => FileChanged(e))"> <div class="col">
<option value="-1">&lt;@Localizer["File.Select"]&gt;</option> <select class="form-select" value="@FileId" @onchange="(e => FileChanged(e))">
@foreach (File file in _files) <option value="-1">&lt;@Localizer["File.Select"]&gt;</option>
{ @foreach (File file in _files)
<option value="@(file.FileId)">@(file.Name)</option> {
} <option value="@(file.FileId)">@(file.Name)</option>
</select> }
</div> </select>
} </div>
@if (ShowUpload && _haseditpermission) </div>
{ }
<div> @if (ShowUpload && _haseditpermission)
@if (UploadMultiple) {
{ <div class="row">
<input type="file" id="@_fileinputid" name="file" accept="@_filter" multiple /> <div class="col mt-2">
} @if (UploadMultiple)
else {
{ <input type="file" id="@_fileinputid" name="file" accept="@_filter" multiple />
<input type="file" id="@_fileinputid" name="file" accept="@_filter" /> }
} else
<span id="@_progressinfoid"></span><progress id="@_progressbarid" style="width: 150px; visibility: hidden;"></progress> {
<span class="float-end"> <input type="file" id="@_fileinputid" name="file" accept="@_filter" />
<button type="button" class="btn btn-success" @onclick="UploadFile">@SharedLocalizer["Upload"]</button> }
@if (ShowFiles && GetFileId() != -1) </div>
{ <div class="col mt-2 text-center">
<button type="button" class="btn btn-danger" @onclick="DeleteFile">@SharedLocalizer["Delete"]</button> <button type="button" class="btn btn-success" @onclick="UploadFile">@SharedLocalizer["Upload"]</button>
} @if (ShowFiles && GetFileId() != -1)
</span> {
</div> <button type="button" class="btn btn-danger mx-1" @onclick="DeleteFile">@SharedLocalizer["Delete"]</button>
} }
</div>
</div>
<div class="row">
<div class="col mt-1"><span id="@_progressinfoid" style="display: none;"></span></div>
<div class="col text-center mt-1"><progress id="@_progressbarid" class="mt-1" style="display: none;"></progress></div>
</div>
}
</div>
</div> </div>
@if (_image != string.Empty) @if (_image != string.Empty)
{ {
@ -68,8 +76,8 @@
</div> </div>
@if (!string.IsNullOrEmpty(_message)) @if (!string.IsNullOrEmpty(_message))
{ {
<div class="row"> <div class="row mt-1">
<div class="col mt-2"> <div class="col">
<ModuleMessage Message="@_message" Type="@_messagetype" /> <ModuleMessage Message="@_message" Type="@_messagetype" />
</div> </div>
</div> </div>
@ -96,10 +104,16 @@
public string Id { get; set; } // optional - for setting the id of the FileManager component for accessibility public string Id { get; set; } // optional - for setting the id of the FileManager component for accessibility
[Parameter] [Parameter]
public string Folder { get; set; } // optional - for setting a specific folder by default ( only relevant for host functions ) public int FolderId { get; set; } = -1; // optional - for setting a specific default folder by folderid
[Parameter] [Parameter]
public int FolderId { get; set; } = -1; // optional - for setting a specific folderid by default public string Folder { get; set; } = ""; // optional - for setting a specific default folder by folder path
[Parameter]
public int FileId { get; set; } = -1; // optional - for selecting a specific file by default
[Parameter]
public string Filter { get; set; } // optional - comma delimited list of file types that can be selected or uploaded ie. "jpg,gif"
[Parameter] [Parameter]
public bool ShowFiles { get; set; } = true; // optional - for indicating whether a list of files should be displayed - default is true public bool ShowFiles { get; set; } = true; // optional - for indicating whether a list of files should be displayed - default is true
@ -114,10 +128,7 @@
public bool ShowImage { get; set; } = true; // optional - for indicating whether an image thumbnail should be displayed - default is true public bool ShowImage { get; set; } = true; // optional - for indicating whether an image thumbnail should be displayed - default is true
[Parameter] [Parameter]
public int FileId { get; set; } = -1; // optional - for selecting a specific file by default public bool ShowSuccess { get; set; } = false; // optional - for indicating whether a success message should be displayed upon successful upload - default is false
[Parameter]
public string Filter { get; set; } // optional - comma delimited list of file types that can be selected or uploaded ie. "jpg,gif"
[Parameter] [Parameter]
public bool UploadMultiple { get; set; } = false; // optional - enable multiple file uploads - default false public bool UploadMultiple { get; set; } = false; // optional - enable multiple file uploads - default false
@ -138,19 +149,35 @@
_id = Id; _id = Id;
} }
// packages folder is a framework folder for uploading installable nuget packages
if (Folder == Constants.PackagesFolder)
{
ShowFiles = false;
ShowFolders = false;
Filter = "nupkg";
ShowSuccess = true;
}
if (!ShowFiles) if (!ShowFiles)
{ {
ShowImage = false; ShowImage = false;
} }
if (!string.IsNullOrEmpty(Folder)) _folders = await FolderService.GetFoldersAsync(ModuleState.SiteId);
if (!string.IsNullOrEmpty(Folder) && Folder != Constants.PackagesFolder)
{ {
_folders = new List<Folder> { new Folder { FolderId = -1, Name = Folder } }; Folder folder = await FolderService.GetFolderAsync(ModuleState.SiteId, Folder);
FolderId = -1; if (folder != null)
} {
else FolderId = folder.FolderId;
{ }
_folders = await FolderService.GetFoldersAsync(ModuleState.SiteId); else
{
FolderId = -1;
_message = "Folder Path " + Folder + "Does Not Exist";
_messagetype = MessageType.Error;
}
} }
if (FileId != -1) if (FileId != -1)
@ -164,6 +191,8 @@
else else
{ {
FileId = -1; // file does not exist FileId = -1; // file does not exist
_message = "FileId " + FileId.ToString() + "Does Not Exist";
_messagetype = MessageType.Error;
} }
} }
@ -186,10 +215,10 @@
private async Task GetFiles() private async Task GetFiles()
{ {
_haseditpermission = false; _haseditpermission = false;
if (!string.IsNullOrEmpty(Folder)) if (Folder == Constants.PackagesFolder)
{ {
_haseditpermission = UserSecurity.IsAuthorized(PageState.User, RoleNames.Host); _haseditpermission = UserSecurity.IsAuthorized(PageState.User, RoleNames.Host);
_files = await FileService.GetFilesAsync(Folder); _files = new List<File>();
} }
else else
{ {
@ -234,7 +263,6 @@
catch (Exception ex) catch (Exception ex)
{ {
await logger.LogError(ex, "Error Loading Files {Error}", ex.Message); await logger.LogError(ex, "Error Loading Files {Error}", ex.Message);
_message = Localizer["Error.File.Load"]; _message = Localizer["Error.File.Load"];
_messagetype = MessageType.Error; _messagetype = MessageType.Error;
} }
@ -286,7 +314,7 @@
try try
{ {
string result; string result;
if (!string.IsNullOrEmpty(Folder)) if (Folder == Constants.PackagesFolder)
{ {
result = await FileService.UploadFilesAsync(Folder, upload, _guid); result = await FileService.UploadFilesAsync(Folder, upload, _guid);
} }
@ -298,9 +326,11 @@
if (result == string.Empty) if (result == string.Empty)
{ {
await logger.LogInformation("File Upload Succeeded {Files}", upload); await logger.LogInformation("File Upload Succeeded {Files}", upload);
if (ShowSuccess)
_message = Localizer["Success.File.Upload"]; {
_messagetype = MessageType.Success; _message = Localizer["Success.File.Upload"];
_messagetype = MessageType.Success;
}
// set FileId to first file in upload collection // set FileId to first file in upload collection
await GetFiles(); await GetFiles();
@ -364,5 +394,7 @@
public int GetFileId() => FileId; public int GetFileId() => FileId;
public int GetFolderId() => FolderId;
public File GetFile() => _file; public File GetFile() => _file;
} }

View File

@ -4,13 +4,13 @@
@if (ItemList != null) @if (ItemList != null)
{ {
@if (Toolbar == "Top" && _pages > 0 && Items.Count() > _maxItems) @if ((Toolbar == "Top" || Toolbar == "Both") && _pages > 0 && Items.Count() > _maxItems)
{ {
<ul class="pagination justify-content-center my-2"> <ul class="pagination justify-content-center my-2">
<li class="page-item@((_page > 1) ? "" : " disabled")"> <li class="page-item@((_page > 1) ? "" : " disabled")">
<a class="page-link" @onclick=@(async () => UpdateList(1))><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a> <a class="page-link" @onclick=@(async () => UpdateList(1))><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
</li> </li>
@if (_pages > _displayPages) @if (_pages > _displayPages && _displayPages > 1)
{ {
<li class="page-item@((_page > _displayPages) ? "" : " disabled")"> <li class="page-item@((_page > _displayPages) ? "" : " disabled")">
<a class="page-link" @onclick=@(async () => SkipPages("back"))><span class="oi oi-media-skip-backward" title="skip back" aria-hidden="true"></span></a> <a class="page-link" @onclick=@(async () => SkipPages("back"))><span class="oi oi-media-skip-backward" title="skip back" aria-hidden="true"></span></a>
@ -38,7 +38,7 @@
<li class="page-item@((_page < _pages) ? "" : " disabled")"> <li class="page-item@((_page < _pages) ? "" : " disabled")">
<a class="page-link" @onclick=@(async () => NavigateToPage("next"))><span class="oi oi-chevron-right" title="next" aria-hidden="true"></span></a> <a class="page-link" @onclick=@(async () => NavigateToPage("next"))><span class="oi oi-chevron-right" title="next" aria-hidden="true"></span></a>
</li> </li>
@if (_pages > _displayPages) @if (_pages > _displayPages && _displayPages > 1)
{ {
<li class="page-item@((_endPage < _pages) ? "" : " disabled")"> <li class="page-item@((_endPage < _pages) ? "" : " disabled")">
<a class="page-link" @onclick=@(async () => SkipPages("forward"))><span class="oi oi-media-skip-forward" title="skip forward" aria-hidden="true"></span></a> <a class="page-link" @onclick=@(async () => SkipPages("forward"))><span class="oi oi-media-skip-forward" title="skip forward" aria-hidden="true"></span></a>
@ -48,7 +48,7 @@
<a class="page-link" @onclick=@(async () => UpdateList(_pages))><span class="oi oi-media-step-forward" title="end" aria-hidden="true"></span></a> <a class="page-link" @onclick=@(async () => UpdateList(_pages))><span class="oi oi-media-step-forward" title="end" aria-hidden="true"></span></a>
</li> </li>
<li class="page-item disabled"> <li class="page-item disabled">
<a class="page-link">Page @_page of @_pages</a> <a class="page-link" style="white-space: nowrap;">Page @_page of @_pages</a>
</li> </li>
</ul> </ul>
} }
@ -101,7 +101,7 @@
} }
</div> </div>
} }
@if (Toolbar == "Bottom" && _pages > 0 && Items.Count() > _maxItems) @if ((Toolbar == "Bottom" || Toolbar == "Both") && _pages > 0 && Items.Count() > _maxItems)
{ {
<ul class="pagination justify-content-center my-2"> <ul class="pagination justify-content-center my-2">
<li class="page-item@((_page > 1) ? "" : " disabled")"> <li class="page-item@((_page > 1) ? "" : " disabled")">
@ -164,7 +164,7 @@
public string Format { get; set; } // Table or Grid public string Format { get; set; } // Table or Grid
[Parameter] [Parameter]
public string Toolbar { get; set; } // Top or Bottom public string Toolbar { get; set; } // Top, Bottom or Both
[Parameter] [Parameter]
public RenderFragment Header { get; set; } = null; public RenderFragment Header { get; set; } = null;
@ -215,7 +215,7 @@
} }
else else
{ {
Class = "container"; Class = "container-fluid px-0";
} }
} }

View File

@ -115,24 +115,24 @@
public override List<Resource> Resources => new List<Resource>() public override List<Resource> Resources => new List<Resource>()
{ {
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill1.3.6.min.js" }, new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill1.3.7.min.js" },
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill-blot-formatter.min.js" }, new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill-blot-formatter.min.js" },
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill-interop.js" } new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill-interop.js" }
}; };
protected override void OnInitialized() protected override void OnParametersSet()
{ {
_content = Content; // raw HTML _content = Content; // raw HTML
} }
protected override async Task OnAfterRenderAsync(bool firstRender) protected override async Task OnAfterRenderAsync(bool firstRender)
{ {
if (firstRender) var interop = new RichTextEditorInterop(JSRuntime);
if (firstRender)
{ {
await base.OnAfterRenderAsync(firstRender); await base.OnAfterRenderAsync(firstRender);
var interop = new RichTextEditorInterop(JSRuntime);
await interop.CreateEditor( await interop.CreateEditor(
_editorElement, _editorElement,
_toolBar, _toolBar,
@ -140,14 +140,15 @@
Placeholder, Placeholder,
Theme, Theme,
DebugLevel); DebugLevel);
await interop.LoadEditorContent(_editorElement, Content);
_content = Content; // raw HTML
// preserve a copy of the rich text content ( Quill sanitizes content so we need to retrieve it from the editor )
_original = await interop.GetHtml(_editorElement);
} }
await interop.LoadEditorContent(_editorElement, Content);
_content = Content; // raw HTML
// preserve a copy of the rich text content ( Quill sanitizes content so we need to retrieve it from the editor )
_original = await interop.GetHtml(_editorElement);
} }
public void CloseFileManager() public void CloseFileManager()

View File

@ -38,7 +38,7 @@ else
if (string.IsNullOrEmpty(Heading)) if (string.IsNullOrEmpty(Heading))
{ {
Name = Localize(nameof(Name), Name); Heading = Localize(nameof(Name), Name);
} }
else else
{ {

View File

@ -43,16 +43,12 @@
[Parameter] [Parameter]
public bool Refresh { get; set; } // optional - used in scenarios where TabPanels are added/removed dynamically within a parent form. ActiveTab may need to be reset as well when this property is used. public bool Refresh { get; set; } // optional - used in scenarios where TabPanels are added/removed dynamically within a parent form. ActiveTab may need to be reset as well when this property is used.
protected override void OnInitialized() protected override void OnParametersSet()
{ {
if (PageState.QueryString.ContainsKey("tab")) if (PageState.QueryString.ContainsKey("tab"))
{ {
ActiveTab = PageState.QueryString["tab"]; ActiveTab = PageState.QueryString["tab"];
} }
}
protected override void OnParametersSet()
{
if (_tabPanels == null || Refresh) if (_tabPanels == null || Refresh)
{ {
_tabPanels = new List<TabPanel>(); _tabPanels = new List<TabPanel>();

View File

@ -30,8 +30,8 @@
public override List<Resource> Resources => new List<Resource>() public override List<Resource> Resources => new List<Resource>()
{ {
new Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css" }, new Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css" },
new Resource { ResourceType = ResourceType.Stylesheet, Url = "css/quill/quill1.3.6.bubble.css" }, new Resource { ResourceType = ResourceType.Stylesheet, Url = "css/quill/quill1.3.7.bubble.css" },
new Resource { ResourceType = ResourceType.Stylesheet, Url = "css/quill/quill1.3.6.snow.css" } new Resource { ResourceType = ResourceType.Stylesheet, Url = "css/quill/quill1.3.7.snow.css" }
}; };
private RichTextEditor RichTextEditorHtml; private RichTextEditor RichTextEditorHtml;
@ -65,8 +65,8 @@
} }
catch (Exception ex) catch (Exception ex)
{ {
await logger.LogError(ex, "An Error Occurred Loading Html/Text Content. " + ex.Message); await logger.LogError(ex, "Error Loading Content {Error}", ex.Message);
AddModuleMessage(ex.Message, MessageType.Error); AddModuleMessage(Localizer["Error.Content.Load"], MessageType.Error);
} }
} }
@ -91,7 +91,7 @@
await HtmlTextService.AddHtmlTextAsync(htmltext); await HtmlTextService.AddHtmlTextAsync(htmltext);
} }
await logger.LogInformation("Html/Text Content Saved {HtmlText}", htmltext); await logger.LogInformation("Content Saved {HtmlText}", htmltext);
NavigationManager.NavigateTo(NavigateUrl()); NavigationManager.NavigateTo(NavigateUrl());
} }
catch (Exception ex) catch (Exception ex)

View File

@ -15,16 +15,16 @@
} }
@code { @code {
public override List<Resource> Resources => new List<Resource>() public override List<Resource> Resources => new List<Resource>()
{ {
new Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css" } new Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css" }
}; };
private string content = ""; private string content = "";
protected override async Task OnParametersSetAsync() protected override async Task OnParametersSetAsync()
{ {
try try
{ {
var htmltext = await HtmlTextService.GetHtmlTextAsync(ModuleState.ModuleId); var htmltext = await HtmlTextService.GetHtmlTextAsync(ModuleState.ModuleId);
if (htmltext != null) if (htmltext != null)
@ -35,8 +35,8 @@
} }
catch (Exception ex) catch (Exception ex)
{ {
await logger.LogError(ex, "An Error Occurred Loading Html/Text Content. " + ex.Message); await logger.LogError(ex, "Error Loading Content {Error}", ex.Message);
AddModuleMessage(ex.Message, MessageType.Error); AddModuleMessage(Localizer["Error.Content.Load"], MessageType.Error);
} }
} }
} }

View File

@ -1,7 +1,9 @@
using Oqtane.Documentation;
using Oqtane.Models; using Oqtane.Models;
namespace Oqtane.Modules.HtmlText namespace Oqtane.Modules.HtmlText
{ {
[PrivateApi("Mark HtmlText classes as private, since it's not very useful in the public docs")]
public class ModuleInfo : IModule public class ModuleInfo : IModule
{ {
public ModuleDefinition ModuleDefinition => new ModuleDefinition public ModuleDefinition ModuleDefinition => new ModuleDefinition

View File

@ -1,10 +1,12 @@
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using Oqtane.Documentation;
using Oqtane.Services; using Oqtane.Services;
using Oqtane.Shared; using Oqtane.Shared;
namespace Oqtane.Modules.HtmlText.Services namespace Oqtane.Modules.HtmlText.Services
{ {
[PrivateApi("Mark HtmlText classes as private, since it's not very useful in the public docs")]
public class HtmlTextService : ServiceBase, IHtmlTextService, IService public class HtmlTextService : ServiceBase, IHtmlTextService, IService
{ {
public HtmlTextService(HttpClient http, SiteState siteState) : base(http, siteState) {} public HtmlTextService(HttpClient http, SiteState siteState) : base(http, siteState) {}

View File

@ -1,9 +1,11 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Oqtane.Documentation;
using Oqtane.Modules.HtmlText.Models; using Oqtane.Modules.HtmlText.Models;
namespace Oqtane.Modules.HtmlText.Services namespace Oqtane.Modules.HtmlText.Services
{ {
[PrivateApi("Mark HtmlText classes as private, since it's not very useful in the public docs")]
public interface IHtmlTextService public interface IHtmlTextService
{ {
Task<Models.HtmlText> GetHtmlTextAsync(int ModuleId); Task<Models.HtmlText> GetHtmlTextAsync(int ModuleId);

View File

@ -30,8 +30,8 @@ namespace Oqtane.Modules
[CascadingParameter] [CascadingParameter]
protected Module ModuleState { get; set; } protected Module ModuleState { get; set; }
[CascadingParameter] [Parameter]
protected ModuleInstance ModuleInstance { get; set; } public ModuleInstance ModuleInstance { get; set; }
// optional interface properties // optional interface properties
public virtual SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.View; } set { } } // default security public virtual SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.View; } set { } } // default security
@ -136,7 +136,12 @@ namespace Oqtane.Modules
public string ImageUrl(int fileid, int width, int height, string mode) public string ImageUrl(int fileid, int width, int height, string mode)
{ {
return Utilities.ImageUrl(PageState.Alias, fileid, width, height, mode); return ImageUrl(fileid, width, height, mode, 0);
}
public string ImageUrl(int fileid, int width, int height, string mode, int rotate)
{
return Utilities.ImageUrl(PageState.Alias, fileid, width, height, mode, rotate);
} }
public virtual Dictionary<string, string> GetUrlParameters(string parametersTemplate = "") public virtual Dictionary<string, string> GetUrlParameters(string parametersTemplate = "")

View File

@ -1,11 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly"> <Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<RazorLangVersion>3.0</RazorLangVersion> <RazorLangVersion>3.0</RazorLangVersion>
<Configurations>Debug;Release</Configurations> <Configurations>Debug;Release</Configurations>
<Version>2.3.0</Version> <Version>3.0.1</Version>
<Product>Oqtane</Product> <Product>Oqtane</Product>
<Authors>Shaun Walker</Authors> <Authors>Shaun Walker</Authors>
<Company>.NET Foundation</Company> <Company>.NET Foundation</Company>
@ -13,7 +13,7 @@
<Copyright>.NET Foundation</Copyright> <Copyright>.NET Foundation</Copyright>
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl> <PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl> <PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v2.3.0</PackageReleaseNotes> <PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.0.1</PackageReleaseNotes>
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl> <RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
<RepositoryType>Git</RepositoryType> <RepositoryType>Git</RepositoryType>
<RootNamespace>Oqtane</RootNamespace> <RootNamespace>Oqtane</RootNamespace>
@ -22,23 +22,18 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="5.0.4" /> <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="6.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="5.0.4" PrivateAssets="all" /> <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="6.0.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="5.0.4" /> <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="6.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Localization" Version="2.2.0" /> <PackageReference Include="Microsoft.AspNetCore.Localization" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Localization" Version="5.0.4" /> <PackageReference Include="Microsoft.Extensions.Localization" Version="6.0.0" />
<PackageReference Include="System.Net.Http.Json" Version="5.0.0" /> <PackageReference Include="System.Net.Http.Json" Version="6.0.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Oqtane.Shared\Oqtane.Shared.csproj" /> <ProjectReference Include="..\Oqtane.Shared\Oqtane.Shared.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Folder Include="Resources\" />
<Folder Include="Resources\Themes\Controls\Theme\" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<TrimmerRootAssembly Include="System.Runtime" /> <TrimmerRootAssembly Include="System.Runtime" />
<TrimmerRootAssembly Include="System.Linq.Parallel" /> <TrimmerRootAssembly Include="System.Linq.Parallel" />

View File

@ -12,6 +12,7 @@ using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Microsoft.AspNetCore.Localization; using Microsoft.AspNetCore.Localization;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.JSInterop; using Microsoft.JSInterop;
using Oqtane.Documentation;
using Oqtane.Modules; using Oqtane.Modules;
using Oqtane.Services; using Oqtane.Services;
using Oqtane.Shared; using Oqtane.Shared;
@ -19,12 +20,12 @@ using Oqtane.UI;
namespace Oqtane.Client namespace Oqtane.Client
{ {
[PrivateApi("Mark Entry-Program as private, since it's not very useful in the public docs")]
public class Program public class Program
{ {
public static async Task Main(string[] args) public static async Task Main(string[] args)
{ {
var builder = WebAssemblyHostBuilder.CreateDefault(args); var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("app");
var httpClient = new HttpClient {BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)}; var httpClient = new HttpClient {BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)};

View File

@ -153,4 +153,22 @@
<data name="Integrated" xml:space="preserve"> <data name="Integrated" xml:space="preserve">
<value>Integrated</value> <value>Integrated</value>
</data> </data>
<data name="Encryption,Text" xml:space="preserve">
<value>Encryption:</value>
</data>
<data name="Encryption.HelpText" xml:space="preserve">
<value>Specify if you are using an encrypted database connection. It is highly recommended to use encryption in a production environment.</value>
</data>
<data name="Self Signed" xml:space="preserve">
<value>Self Signed</value>
</data>
<data name="TrustServerCertificate.HelpText" xml:space="preserve">
<value>Specify the type of certificate you are using for encryption</value>
</data>
<data name="TrustServerCertificate.Text" xml:space="preserve">
<value>Trust Server Certificate:</value>
</data>
<data name="Verifiable" xml:space="preserve">
<value>Verifiable</value>
</data>
</root> </root>

View File

@ -159,4 +159,10 @@
<data name="Type" xml:space="preserve"> <data name="Type" xml:space="preserve">
<value>Type</value> <value>Type</value>
</data> </data>
<data name="DeleteFile.Text" xml:space="preserve">
<value>Delete</value>
</data>
<data name="UploadFiles.Text" xml:space="preserve">
<value>Upload Files</value>
</data>
</root> </root>

View File

@ -168,4 +168,13 @@
<data name="Stop" xml:space="preserve"> <data name="Stop" xml:space="preserve">
<value>Stop</value> <value>Stop</value>
</data> </data>
<data name="DeleteJob.Text" xml:space="preserve">
<value>Delete</value>
</data>
<data name="EditJob.Text" xml:space="preserve">
<value>Edit</value>
</data>
<data name="JobLog.Text" xml:space="preserve">
<value>Log</value>
</data>
</root> </root>

View File

@ -153,4 +153,19 @@
<data name="Search.NoResults" xml:space="preserve"> <data name="Search.NoResults" xml:space="preserve">
<value>No Translations Match The Criteria Provided Or Package Service Is Disabled</value> <value>No Translations Match The Criteria Provided Or Package Service Is Disabled</value>
</data> </data>
<data name="Download.Heading" xml:space="preserve">
<value>Download</value>
</data>
<data name="LanguageUpload.HelpText" xml:space="preserve">
<value>Upload one or more translations. Once they are uploaded click Install to complete the installation.</value>
</data>
<data name="LanguageUpload.Text" xml:space="preserve">
<value>Upload Language</value>
</data>
<data name="Manage.Heading" xml:space="preserve">
<value>Manage</value>
</data>
<data name="Upload.Heading" xml:space="preserve">
<value>Upload</value>
</data>
</root> </root>

View File

@ -141,4 +141,7 @@
<data name="Default" xml:space="preserve"> <data name="Default" xml:space="preserve">
<value>Default</value> <value>Default</value>
</data> </data>
<data name="DeleteLanguage.Text" xml:space="preserve">
<value>Delete</value>
</data>
</root> </root>

View File

@ -189,4 +189,7 @@
<data name="Create" xml:space="preserve"> <data name="Create" xml:space="preserve">
<value>Create</value> <value>Create</value>
</data> </data>
<data name="LogDetails.Text" xml:space="preserve">
<value>Details</value>
</data>
</root> </root>

View File

@ -138,4 +138,10 @@
<data name="Search.NoResults" xml:space="preserve"> <data name="Search.NoResults" xml:space="preserve">
<value>No Modules Match The Criteria Provided Or Package Service Is Disabled</value> <value>No Modules Match The Criteria Provided Or Package Service Is Disabled</value>
</data> </data>
<data name="Download.Heading" xml:space="preserve">
<value>Download</value>
</data>
<data name="Upload.Heading" xml:space="preserve">
<value>Upload</value>
</data>
</root> </root>

View File

@ -183,7 +183,16 @@
<data name="Runtimes.Text" xml:space="preserve"> <data name="Runtimes.Text" xml:space="preserve">
<value>Runtimes: </value> <value>Runtimes: </value>
</data> </data>
<data name="Definition.Name" xml:space="preserve"> <data name="Definition.Heading" xml:space="preserve">
<value>Definition</value> <value>Definition</value>
</data> </data>
<data name="Information.Heading" xml:space="preserve">
<value>Information</value>
</data>
<data name="Permissions.Heading" xml:space="preserve">
<value>Permissions</value>
</data>
<data name="Information.Text" xml:space="preserve">
<value>Information</value>
</data>
</root> </root>

View File

@ -144,4 +144,10 @@
<data name="DeleteModule.Header" xml:space="preserve"> <data name="DeleteModule.Header" xml:space="preserve">
<value>Delete Module</value> <value>Delete Module</value>
</data> </data>
<data name="InUse" xml:space="preserve">
<value>In Use</value>
</data>
<data name="EditModule.Text" xml:space="preserve">
<value>Edit</value>
</data>
</root> </root>

View File

@ -181,7 +181,7 @@
<value>Select whether the page is part of the site navigation or hidden</value> <value>Select whether the page is part of the site navigation or hidden</value>
</data> </data>
<data name="UrlPath.HelpText" xml:space="preserve"> <data name="UrlPath.HelpText" xml:space="preserve">
<value>Optionally enter a url path for this page (ie. home ). If you do not provide a url path, the page name will be used.</value> <value>Optionally enter a url path for this page (ie. home ). If you do not provide a url path, the page name will be used. If the page is intended to be the root path specify '/'.</value>
</data> </data>
<data name="Redirect.HelpText" xml:space="preserve"> <data name="Redirect.HelpText" xml:space="preserve">
<value>Optionally enter a url which this page should redirect to when a user navigates to it</value> <value>Optionally enter a url which this page should redirect to when a user navigates to it</value>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<root> <root>
<!-- <!--
Microsoft ResX Schema Microsoft ResX Schema
@ -169,7 +169,7 @@
<value>Select whether the page is part of the site navigation or hidden</value> <value>Select whether the page is part of the site navigation or hidden</value>
</data> </data>
<data name="UrlPath.HelpText" xml:space="preserve"> <data name="UrlPath.HelpText" xml:space="preserve">
<value>Optionally enter a url path for this page (ie. home ). If you do not provide a url path, the page name will be used.</value> <value>Optionally enter a url path for this page (ie. home ). If you do not provide a url path, the page name will be used. If the page is intended to be the root path specify '/'.</value>
</data> </data>
<data name="Redirect.HelpText" xml:space="preserve"> <data name="Redirect.HelpText" xml:space="preserve">
<value>Optionally enter a url which this page should redirect to when a user navigates to it</value> <value>Optionally enter a url which this page should redirect to when a user navigates to it</value>
@ -210,7 +210,7 @@
<data name="Personalizable.Text" xml:space="preserve"> <data name="Personalizable.Text" xml:space="preserve">
<value>Personalizable? </value> <value>Personalizable? </value>
</data> </data>
<data name="Appearance.Name" xml:space="preserve"> <data name="Appearance.Heading" xml:space="preserve">
<value>Appearance</value> <value>Appearance</value>
</data> </data>
<data name="ThisLocation.Keep" xml:space="preserve"> <data name="ThisLocation.Keep" xml:space="preserve">
@ -231,4 +231,40 @@
<data name="Move.Text" xml:space="preserve"> <data name="Move.Text" xml:space="preserve">
<value>Move: </value> <value>Move: </value>
</data> </data>
<data name="ModuleDefinition" xml:space="preserve">
<value>Module</value>
</data>
<data name="ModuleTitle" xml:space="preserve">
<value>Title</value>
</data>
<data name="PageModules.Heading" xml:space="preserve">
<value>Modules</value>
</data>
<data name="ModuleSettings.Text" xml:space="preserve">
<value>Edit</value>
</data>
<data name="DeleteModule.Header" xml:space="preserve">
<value>Delete Module</value>
</data>
<data name="DeleteModule.Message" xml:space="preserve">
<value>Are You Sure You Wish To Delete This Module?</value>
</data>
<data name="DeleteModule.Text" xml:space="preserve">
<value>Delete</value>
</data>
<data name="Permissions.Heading" xml:space="preserve">
<value>Permissions</value>
</data>
<data name="ThemeSettings.Heading" xml:space="preserve">
<value>Theme Settings</value>
</data>
<data name="Appearance.Hea" xml:space="preserve">
<value />
</data>
<data name="Clickable.HelpText" xml:space="preserve">
<value>Select whether the link in the site navigation is enabled or disabled</value>
</data>
<data name="Clickable.Text" xml:space="preserve">
<value>Clickable?</value>
</data>
</root> </root>

View File

@ -129,4 +129,13 @@
<data name="DeletePage.Header" xml:space="preserve"> <data name="DeletePage.Header" xml:space="preserve">
<value>Delete Page</value> <value>Delete Page</value>
</data> </data>
<data name="Browse" xml:space="preserve">
<value>Browse</value>
</data>
<data name="DeletePage.Text" xml:space="preserve">
<value>Delete</value>
</data>
<data name="EditPage.Text" xml:space="preserve">
<value>Edit</value>
</data>
</root> </root>

View File

@ -132,4 +132,10 @@
<data name="DeleteProfile.Header" xml:space="preserve"> <data name="DeleteProfile.Header" xml:space="preserve">
<value>Delete Profile</value> <value>Delete Profile</value>
</data> </data>
<data name="DeleteProfile.Text" xml:space="preserve">
<value>Delete</value>
</data>
<data name="EditProfile.Text" xml:space="preserve">
<value>Edit</value>
</data>
</root> </root>

View File

@ -177,7 +177,10 @@
<data name="DeleteAllModules.Message" xml:space="preserve"> <data name="DeleteAllModules.Message" xml:space="preserve">
<value>Are You Sure You Wish To Permanently Delete All Modules?</value> <value>Are You Sure You Wish To Permanently Delete All Modules?</value>
</data> </data>
<data name="Pages.Name" xml:space="preserve"> <data name="Pages.Heading" xml:space="preserve">
<value>Pages</value> <value>Pages</value>
</data> </data>
<data name="Modules.Heading" xml:space="preserve">
<value>Modules</value>
</data>
</root> </root>

View File

@ -129,4 +129,13 @@
<data name="DeleteRole.Header" xml:space="preserve"> <data name="DeleteRole.Header" xml:space="preserve">
<value>Delete Role</value> <value>Delete Role</value>
</data> </data>
<data name="DeleteRole.Text" xml:space="preserve">
<value>Delete</value>
</data>
<data name="Edit.Text" xml:space="preserve">
<value>Edit</value>
</data>
<data name="Users.Text" xml:space="preserve">
<value>Users</value>
</data>
</root> </root>

View File

@ -129,7 +129,7 @@
<data name="DefaultContainer.Text" xml:space="preserve"> <data name="DefaultContainer.Text" xml:space="preserve">
<value>Default Container: </value> <value>Default Container: </value>
</data> </data>
<data name="Appearance.Name" xml:space="preserve"> <data name="Appearance.Heading" xml:space="preserve">
<value>Appearance</value> <value>Appearance</value>
</data> </data>
<data name="DefaultAdminContainer" xml:space="preserve"> <data name="DefaultAdminContainer" xml:space="preserve">
@ -169,10 +169,7 @@
<value>Enter the tenant for the site</value> <value>Enter the tenant for the site</value>
</data> </data>
<data name="Aliases.HelpText" xml:space="preserve"> <data name="Aliases.HelpText" xml:space="preserve">
<value>Enter the alias for the server</value> <value>The aliases for the site. An alias can be a domain name (www.site.com) or a virtual folder (ie. www.site.com/folder). If a site has multiple aliases they should be separated by commas.</value>
</data>
<data name="AllowRegistration.HelpText" xml:space="preserve">
<value>Do you want the users to be able to register for an account on the site</value>
</data> </data>
<data name="IsDeleted.HelpText" xml:space="preserve"> <data name="IsDeleted.HelpText" xml:space="preserve">
<value>Is this site deleted?</value> <value>Is this site deleted?</value>
@ -225,9 +222,6 @@
<data name="Aliases.Text" xml:space="preserve"> <data name="Aliases.Text" xml:space="preserve">
<value>Aliases: </value> <value>Aliases: </value>
</data> </data>
<data name="AllowRegistration.Text" xml:space="preserve">
<value>Allow User Registration? </value>
</data>
<data name="IsDeleted.Text" xml:space="preserve"> <data name="IsDeleted.Text" xml:space="preserve">
<value>Is Deleted? </value> <value>Is Deleted? </value>
</data> </data>
@ -282,4 +276,46 @@
<data name="Theme.Select" xml:space="preserve"> <data name="Theme.Select" xml:space="preserve">
<value>Select Theme</value> <value>Select Theme</value>
</data> </data>
<data name="Hosting.Heading" xml:space="preserve">
<value>Hosting Model</value>
</data>
<data name="Prerender.HelpText" xml:space="preserve">
<value>Specifies if the site should be prerendered (for search crawlers, etc...)</value>
</data>
<data name="Prerender.Text" xml:space="preserve">
<value>Prerender? </value>
</data>
<data name="Runtime.HelpText" xml:space="preserve">
<value>The Blazor runtime hosting model</value>
</data>
<data name="Runtime.Text" xml:space="preserve">
<value>Runtime: </value>
</data>
<data name="Browse" xml:space="preserve">
<value>Browse</value>
</data>
<data name="TenantInformation.Heading" xml:space="preserve">
<value>Tenant Information</value>
</data>
<data name="PWASettings.Heading" xml:space="preserve">
<value>PWA Settings</value>
</data>
<data name="SMTPSettings.Heading" xml:space="preserve">
<value>SMTP Settings</value>
</data>
<data name="ConnectionString.Text" xml:space="preserve">
<value>Connection:</value>
</data>
<data name="Database.Text" xml:space="preserve">
<value>Database:</value>
</data>
<data name="ConnectionString.HelpText" xml:space="preserve">
<value>The connection information for the database</value>
</data>
<data name="Database.HelpText" xml:space="preserve">
<value>The database for the tenant</value>
</data>
<data name="DeleteSite.Text" xml:space="preserve">
<value>Delete Site</value>
</data>
</root> </root>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<root> <root>
<!-- <!--
Microsoft ResX Schema Microsoft ResX Schema
@ -217,10 +217,10 @@
<value>Enter the password for the integrated security</value> <value>Enter the password for the integrated security</value>
</data> </data>
<data name="HostUsername.HelpText" xml:space="preserve"> <data name="HostUsername.HelpText" xml:space="preserve">
<value>Enter a valid host username</value> <value>Enter the username of an existing host user</value>
</data> </data>
<data name="HostPassword.HelpText" xml:space="preserve"> <data name="HostPassword.HelpText" xml:space="preserve">
<value>Enter a valid host password</value> <value>Enter the password of an existing host user</value>
</data> </data>
<data name="Name.Text" xml:space="preserve"> <data name="Name.Text" xml:space="preserve">
<value>Site Name: </value> <value>Site Name: </value>
@ -258,4 +258,16 @@
<data name="Error.Database.LoadConfig" xml:space="preserve"> <data name="Error.Database.LoadConfig" xml:space="preserve">
<value>Error loading Database Configuration Control</value> <value>Error loading Database Configuration Control</value>
</data> </data>
<data name="Prerender.HelpText" xml:space="preserve">
<value>Specifies if the site should be prerendered (for search crawlers, etc...)</value>
</data>
<data name="Prerender.Text" xml:space="preserve">
<value>Prerender? </value>
</data>
<data name="Runtime.HelpText" xml:space="preserve">
<value>The Blazor runtime hosting model</value>
</data>
<data name="Runtime.Text" xml:space="preserve">
<value>Runtime: </value>
</data>
</root> </root>

View File

@ -123,9 +123,6 @@
<data name="FrameworkVersion.HelpText" xml:space="preserve"> <data name="FrameworkVersion.HelpText" xml:space="preserve">
<value>Framework Version</value> <value>Framework Version</value>
</data> </data>
<data name="BlazorRuntime.HelpText" xml:space="preserve">
<value>Blazor Runtime (Server or WebAssembly)</value>
</data>
<data name="CLRVersion.HelpText" xml:space="preserve"> <data name="CLRVersion.HelpText" xml:space="preserve">
<value>Common Language Runtime Version</value> <value>Common Language Runtime Version</value>
</data> </data>
@ -141,9 +138,6 @@
<data name="FrameworkVersion.Text" xml:space="preserve"> <data name="FrameworkVersion.Text" xml:space="preserve">
<value>Framework Version: </value> <value>Framework Version: </value>
</data> </data>
<data name="BlazorRuntime.Text" xml:space="preserve">
<value>Blazor Runtime: </value>
</data>
<data name="CLRVersion.Text" xml:space="preserve"> <data name="CLRVersion.Text" xml:space="preserve">
<value>CLR Version: </value> <value>CLR Version: </value>
</data> </data>
@ -165,18 +159,9 @@
<data name="Error.UpdateConfig" xml:space="preserve"> <data name="Error.UpdateConfig" xml:space="preserve">
<value>An Error Occurred Updating The Configuration</value> <value>An Error Occurred Updating The Configuration</value>
</data> </data>
<data name="Server" xml:space="preserve">
<value>Server</value>
</data>
<data name="ServerPrerendered" xml:space="preserve">
<value>ServerPrerendered</value>
</data>
<data name="Success.UpdateConfig.Restart" xml:space="preserve"> <data name="Success.UpdateConfig.Restart" xml:space="preserve">
<value>Configuration Updated. Please Select Restart Application For These Changes To Be Activated.</value> <value>Configuration Updated. Please Select Restart Application For These Changes To Be Activated.</value>
</data> </data>
<data name="WebAssembly" xml:space="preserve">
<value>WebAssembly</value>
</data>
<data name="InstallationId.Text" xml:space="preserve"> <data name="InstallationId.Text" xml:space="preserve">
<value>Installation ID: </value> <value>Installation ID: </value>
</data> </data>
@ -219,10 +204,10 @@
<data name="Critical" xml:space="preserve"> <data name="Critical" xml:space="preserve">
<value>Critical</value> <value>Critical</value>
</data> </data>
<data name="Info" xml:space="preserve"> <data name="Info.Heading" xml:space="preserve">
<value>Info</value> <value>Info</value>
</data> </data>
<data name="Options" xml:space="preserve"> <data name="Options.Heading" xml:space="preserve">
<value>Options</value> <value>Options</value>
</data> </data>
<data name="Register" xml:space="preserve"> <data name="Register" xml:space="preserve">
@ -243,4 +228,7 @@
<data name="Swagger.Text" xml:space="preserve"> <data name="Swagger.Text" xml:space="preserve">
<value>Swagger Enabled?</value> <value>Swagger Enabled?</value>
</data> </data>
<data name="RestartApplication.Text" xml:space="preserve">
<value>Restart Application</value>
</data>
</root> </root>

View File

@ -138,4 +138,10 @@
<data name="DeleteTheme.Header" xml:space="preserve"> <data name="DeleteTheme.Header" xml:space="preserve">
<value>Delete Theme</value> <value>Delete Theme</value>
</data> </data>
<data name="CreateTheme.Text" xml:space="preserve">
<value>Create Theme</value>
</data>
<data name="ViewTheme.Text" xml:space="preserve">
<value>View</value>
</data>
</root> </root>

View File

@ -135,4 +135,10 @@
<data name="Success.Framework.Download" xml:space="preserve"> <data name="Success.Framework.Download" xml:space="preserve">
<value>Framework Downloaded Successfully... Please Select Upgrade To Complete the Process</value> <value>Framework Downloaded Successfully... Please Select Upgrade To Complete the Process</value>
</data> </data>
<data name="Download.Heading" xml:space="preserve">
<value>Download</value>
</data>
<data name="Upload.Heading" xml:space="preserve">
<value>Upload</value>
</data>
</root> </root>

View File

@ -0,0 +1,138 @@
<?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="MappedUrl.Text" xml:space="preserve">
<value>Redirect To:</value>
</data>
<data name="MappedUrl.HelpText" xml:space="preserve">
<value>A fully qualified Url where the user will be redirected</value>
</data>
<data name="Url.HelpText" xml:space="preserve">
<value>A fully qualified Url for this site</value>
</data>
<data name="Url.Text" xml:space="preserve">
<value>Url:</value>
</data>
<data name="Error.SaveUrlMapping" xml:space="preserve">
<value>Error Saving Url Mapping</value>
</data>
<data name="Message.InfoRequired" xml:space="preserve">
<value>Please Provide All Required Information</value>
</data>
</root>

View File

@ -0,0 +1,141 @@
<?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="MappedUrl.Text" xml:space="preserve">
<value>Redirect To:</value>
</data>
<data name="MappedUrl.HelpText" xml:space="preserve">
<value>A fully qualified Url where the user will be redirected</value>
</data>
<data name="Url.HelpText" xml:space="preserve">
<value>A fully qualified Url for this site</value>
</data>
<data name="Url.Text" xml:space="preserve">
<value>Url:</value>
</data>
<data name="Error.LoadUrlMapping" xml:space="preserve">
<value>Error Loading Url Mapping</value>
</data>
<data name="Error.SaveUrlMapping" xml:space="preserve">
<value>Error Saving Url Mapping</value>
</data>
<data name="Message.InfoRequired" xml:space="preserve">
<value>Please Provide All Required Information</value>
</data>
</root>

View File

@ -0,0 +1,162 @@
<?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="Confirm.DeleteUrlMapping" xml:space="preserve">
<value>Are You Sure You Wish To Delete {0}?</value>
</data>
<data name="AddUrlMapping.Text" xml:space="preserve">
<value>Add Url Mapping</value>
</data>
<data name="DeleteUrlMapping.Header" xml:space="preserve">
<value>Delete Url Mapping</value>
</data>
<data name="Requested" xml:space="preserve">
<value>Requested</value>
</data>
<data name="Requests" xml:space="preserve">
<value>Requests</value>
</data>
<data name="Mapped" xml:space="preserve">
<value>Mapped Urls</value>
</data>
<data name="Broken" xml:space="preserve">
<value>Broken Urls</value>
</data>
<data name="CaptureBrokenUrls.HelpText" xml:space="preserve">
<value>Specify if broken Urls should be captured automatically and saved in Url Mappings</value>
</data>
<data name="CaptureBrokenUrls.Text" xml:space="preserve">
<value>Capture Broken Urls?</value>
</data>
<data name="Error.SaveSiteSettings" xml:space="preserve">
<value>Error Saving Settings</value>
</data>
<data name="Settings.Heading" xml:space="preserve">
<value>Settings</value>
</data>
<data name="Success.SaveSiteSettings" xml:space="preserve">
<value>Settings Saved Successfully</value>
</data>
<data name="Urls.Heading" xml:space="preserve">
<value>Urls</value>
</data>
<data name="Url" xml:space="preserve">
<value>Url</value>
</data>
</root>

View File

@ -177,4 +177,10 @@
<data name="Username.Text" xml:space="preserve"> <data name="Username.Text" xml:space="preserve">
<value>Username:</value> <value>Username:</value>
</data> </data>
<data name="Identity.Heading" xml:space="preserve">
<value>Identity</value>
</data>
<data name="Profile.Heading" xml:space="preserve">
<value>Profile</value>
</data>
</root> </root>

View File

@ -126,4 +126,31 @@
<data name="DeleteUser.Header" xml:space="preserve"> <data name="DeleteUser.Header" xml:space="preserve">
<value>Delete User</value> <value>Delete User</value>
</data> </data>
<data name="AllowRegistration.HelpText" xml:space="preserve">
<value>Do you want the users to be able to register for an account on the site</value>
</data>
<data name="AllowRegistration.Text" xml:space="preserve">
<value>Allow User Registration? </value>
</data>
<data name="Error.SaveSiteSettings" xml:space="preserve">
<value>Error Saving Settings</value>
</data>
<data name="Settings.Heading" xml:space="preserve">
<value>Settings</value>
</data>
<data name="Success.SaveSiteSettings" xml:space="preserve">
<value>Settings Saved Successfully</value>
</data>
<data name="Users.Heading" xml:space="preserve">
<value>Users</value>
</data>
<data name="DeleteUser.Text" xml:space="preserve">
<value>Delete</value>
</data>
<data name="EditUser.Text" xml:space="preserve">
<value>Edit</value>
</data>
<data name="Roles.Text" xml:space="preserve">
<value>Roles</value>
</data>
</root> </root>

View File

@ -177,4 +177,7 @@
<data name="Expiry" xml:space="preserve"> <data name="Expiry" xml:space="preserve">
<value>Expiry</value> <value>Expiry</value>
</data> </data>
<data name="DeleteUserRole.Text" xml:space="preserve">
<value>Delete</value>
</data>
</root> </root>

View File

@ -0,0 +1,177 @@
<?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="IP.Text" xml:space="preserve">
<value>IP Address:</value>
</data>
<data name="IP.HelpText" xml:space="preserve">
<value>The last recorded IP address for this visitor</value>
</data>
<data name="Language.HelpText" xml:space="preserve">
<value>The last recorded language for this visitor</value>
</data>
<data name="Language.Text" xml:space="preserve">
<value>Language:</value>
</data>
<data name="Error.LoadVisitor" xml:space="preserve">
<value>Error Loading Visitor</value>
</data>
<data name="Created.HelpText" xml:space="preserve">
<value>The frst recorded date/time when the visitor visited the site</value>
</data>
<data name="Created.Text" xml:space="preserve">
<value>Created:</value>
</data>
<data name="Referrer.HelpText" xml:space="preserve">
<value>The last recorded referrer for this visitor</value>
</data>
<data name="Referrer.Text" xml:space="preserve">
<value>Referrer:</value>
</data>
<data name="Url.HelpText" xml:space="preserve">
<value>The last recorded Url for this visitor</value>
</data>
<data name="Url.Text" xml:space="preserve">
<value>Url:</value>
</data>
<data name="User.HelpText" xml:space="preserve">
<value>The last recorded user associated with this visitor</value>
</data>
<data name="User.Text" xml:space="preserve">
<value>User:</value>
</data>
<data name="UserAgent.HelpText" xml:space="preserve">
<value>The last recorded user agent for this visitor</value>
</data>
<data name="UserAgent.Text" xml:space="preserve">
<value>User Agent:</value>
</data>
<data name="Visited.HelpText" xml:space="preserve">
<value>The last recorded date/time when the visitor visited the site</value>
</data>
<data name="Visited.Text" xml:space="preserve">
<value>Visited:</value>
</data>
<data name="Visits.HelpText" xml:space="preserve">
<value>The total number of visits by this visitor all time</value>
</data>
<data name="Visits.Text" xml:space="preserve">
<value>Visits:</value>
</data>
</root>

View File

@ -0,0 +1,174 @@
<?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="Url" xml:space="preserve">
<value>Url</value>
</data>
<data name="Visited" xml:space="preserve">
<value>Visited</value>
</data>
<data name="Visits" xml:space="preserve">
<value>Visits</value>
</data>
<data name="AllVisitors" xml:space="preserve">
<value>All Visitors</value>
</data>
<data name="PastDay" xml:space="preserve">
<value>Past Day</value>
</data>
<data name="PastMonth" xml:space="preserve">
<value>Past Month</value>
</data>
<data name="PastWeek" xml:space="preserve">
<value>Past Week</value>
</data>
<data name="UsersOnly" xml:space="preserve">
<value>Users Only</value>
</data>
<data name="Language" xml:space="preserve">
<value>Language</value>
</data>
<data name="Error.SaveSiteSettings" xml:space="preserve">
<value>Error Saving Settings</value>
</data>
<data name="Settings.Heading" xml:space="preserve">
<value>Settings</value>
</data>
<data name="Success.SaveSiteSettings" xml:space="preserve">
<value>Settings Saved Successfully</value>
</data>
<data name="Visitors.Heading" xml:space="preserve">
<value>Visitors</value>
</data>
<data name="VisitorTracking.HelpText" xml:space="preserve">
<value>Specify if visitor tracking is enabled</value>
</data>
<data name="VisitorTracking.Text" xml:space="preserve">
<value>Visitor Tracking Enabled?</value>
</data>
<data name="IP" xml:space="preserve">
<value>IP</value>
</data>
<data name="User" xml:space="preserve">
<value>User</value>
</data>
<data name="Created" xml:space="preserve">
<value>Created</value>
</data>
</root>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<root> <root>
<!-- <!--
Microsoft ResX Schema Microsoft ResX Schema
@ -126,9 +126,6 @@
<data name="Error.File.Load" xml:space="preserve"> <data name="Error.File.Load" xml:space="preserve">
<value>Error Loading Files</value> <value>Error Loading Files</value>
</data> </data>
<data name="Success.File.Upload" xml:space="preserve">
<value>File Upload Succeeded</value>
</data>
<data name="Error.File.Upload" xml:space="preserve"> <data name="Error.File.Upload" xml:space="preserve">
<value>File Upload Failed</value> <value>File Upload Failed</value>
</data> </data>
@ -141,4 +138,7 @@
<data name="Error.File.Delete" xml:space="preserve"> <data name="Error.File.Delete" xml:space="preserve">
<value>Error Deleting File</value> <value>Error Deleting File</value>
</data> </data>
<data name="Success.File.Upload" xml:space="preserve">
<value>File Upload Succeeded</value>
</data>
</root> </root>

View File

@ -117,7 +117,10 @@
<resheader name="writer"> <resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader> </resheader>
<data name="Error.Content.Load" xml:space="preserve">
<value>An Error Occurred Loading Content</value>
</data>
<data name="Error.Content.Save" xml:space="preserve"> <data name="Error.Content.Save" xml:space="preserve">
<value>Error Saving Content</value> <value>An Error Occurred Saving Content</value>
</data> </data>
</root> </root>

View File

@ -1,65 +1,65 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<root> <root>
<!-- <!--
Microsoft ResX Schema 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.
Version 2.0 mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
The primary goals of this format is to allow a simple XML format : using a System.ComponentModel.TypeConverter
that is mostly human readable. The generation and parsing of the : and then encoded with base64 encoding.
various data types are done through the TypeConverter classes -->
associated with the data types. <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
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 xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true"> <xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType> <xsd:complexType>
@ -120,4 +120,7 @@
<data name="Edit.Action" xml:space="preserve"> <data name="Edit.Action" xml:space="preserve">
<value>Edit</value> <value>Edit</value>
</data> </data>
<data name="Error.Content.Load" xml:space="preserve">
<value>An Error Occurred Loading Content</value>
</data>
</root> </root>

View File

@ -312,4 +312,13 @@
<data name="Not Specified" xml:space="preserve"> <data name="Not Specified" xml:space="preserve">
<value>Not Specified</value> <value>Not Specified</value>
</data> </data>
<data name="BlazorServer" xml:space="preserve">
<value>Blazor Server</value>
</data>
<data name="BlazorWebAssembly" xml:space="preserve">
<value>Blazor WebAssembly</value>
</data>
<data name="Settings" xml:space="preserve">
<value>Settings</value>
</data>
</root> </root>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<root> <root>
<!-- <!--
Microsoft ResX Schema Microsoft ResX Schema
@ -123,4 +123,7 @@
<data name="Error.Module.InvalidType" xml:space="preserve"> <data name="Error.Module.InvalidType" xml:space="preserve">
<value>Module Type Is Invalid For {0}</value> <value>Module Type Is Invalid For {0}</value>
</data> </data>
<data name="Error.Module.Exception" xml:space="preserve">
<value>An Unexpected Error Has Occurred</value>
</data>
</root> </root>

View File

@ -109,6 +109,9 @@ namespace Oqtane.Services
attempts += 1; attempts += 1;
} }
await interop.SetElementAttribute(id + "ProgressInfo", "style", "display: none;");
await interop.SetElementAttribute(id + "ProgressBar", "style", "display: none;");
if (!success) if (!success)
{ {
result = result.Substring(0, result.Length - 1); result = result.Substring(0, result.Length - 1);

View File

@ -25,8 +25,6 @@ namespace Oqtane.Services
public async Task<Installation> IsInstalled() public async Task<Installation> IsInstalled()
{ {
// add antiforgerytoken header so that it is included on all HttpClient calls for the lifetime of the app
AddRequestHeader(Constants.AntiForgeryTokenHeaderName, _siteState.AntiForgeryToken);
var path = new Uri(_navigationManager.Uri).LocalPath.Substring(1); var path = new Uri(_navigationManager.Uri).LocalPath.Substring(1);
return await GetJsonAsync<Installation>($"{ApiUrl}/installed/?path={WebUtility.UrlEncode(path)}"); return await GetJsonAsync<Installation>($"{ApiUrl}/installed/?path={WebUtility.UrlEncode(path)}");
} }
@ -50,5 +48,14 @@ namespace Oqtane.Services
{ {
await PostJsonAsync($"{ApiUrl}/register?email={WebUtility.UrlEncode(email)}", true); await PostJsonAsync($"{ApiUrl}/register?email={WebUtility.UrlEncode(email)}", true);
} }
public void SetAntiForgeryTokenHeader(string antiforgerytokenvalue)
{
if (!string.IsNullOrEmpty(antiforgerytokenvalue))
{
AddRequestHeader(Constants.AntiForgeryTokenHeaderName, antiforgerytokenvalue);
}
}
} }
} }

View File

@ -41,5 +41,11 @@ namespace Oqtane.Services
/// <param name="email">Email of the user to be registered</param> /// <param name="email">Email of the user to be registered</param>
/// <returns></returns> /// <returns></returns>
Task RegisterAsync(string email); Task RegisterAsync(string email);
/// <summary>
/// Sets the antiforgerytoken header so that it is included on all HttpClient calls for the lifetime of the app
/// </summary>
/// <returns></returns>
void SetAntiForgeryTokenHeader(string antiforgerytokenvalue);
} }
} }

View File

@ -1,4 +1,5 @@
using Oqtane.Models; using Oqtane.Models;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -70,7 +71,7 @@ namespace Oqtane.Services
/// <summary> /// <summary>
/// Returns a key-value dictionary of all module settings for the given module /// Returns a key-value dictionary of all module settings for the given module
/// </summary> /// </summary>
/// <param name="pageId"></param> /// <param name="moduleId"></param>
/// <returns></returns> /// <returns></returns>
Task<Dictionary<string, string>> GetModuleSettingsAsync(int moduleId); Task<Dictionary<string, string>> GetModuleSettingsAsync(int moduleId);
@ -82,6 +83,21 @@ namespace Oqtane.Services
/// <returns></returns> /// <returns></returns>
Task UpdateModuleSettingsAsync(Dictionary<string, string> moduleSettings, int moduleId); Task UpdateModuleSettingsAsync(Dictionary<string, string> moduleSettings, int moduleId);
/// <summary>
/// Returns a key-value dictionary of all module settings for the given module
/// </summary>
/// <param name="moduleDefinitionId"></param>
/// <returns></returns>
Task<Dictionary<string, string>> GetModuleDefinitionSettingsAsync(int moduleDefinitionId);
/// <summary>
/// Updates a module setting
/// </summary>
/// <param name="moduleDefinitionSettings"></param>
/// <param name="moduleDefinitionId"></param>
/// <returns></returns>
Task UpdateModuleDefinitionSettingsAsync(Dictionary<string, string> moduleDefinitionSettings, int moduleDefinitionId);
/// <summary> /// <summary>
/// Returns a key-value dictionary of all user settings for the given user /// Returns a key-value dictionary of all user settings for the given user
/// </summary> /// </summary>
@ -113,6 +129,19 @@ namespace Oqtane.Services
/// <returns></returns> /// <returns></returns>
Task UpdateFolderSettingsAsync(Dictionary<string, string> folderSettings, int folderId); Task UpdateFolderSettingsAsync(Dictionary<string, string> folderSettings, int folderId);
/// <summary>
/// Returns a key-value dictionary of all tenant settings
/// </summary>
/// <returns></returns>
Task<Dictionary<string, string>> GetHostSettingsAsync();
/// <summary>
/// Updates a host setting
/// </summary>
/// <param name="hostSettings"></param>
/// <returns></returns>
Task UpdateHostSettingsAsync(Dictionary<string, string> hostSettings);
/// <summary> /// <summary>
/// Returns a key-value dictionary of all settings for the given entityName /// Returns a key-value dictionary of all settings for the given entityName
/// </summary> /// </summary>
@ -135,7 +164,7 @@ namespace Oqtane.Services
/// </summary> /// </summary>
/// <param name="settingId"></param> /// <param name="settingId"></param>
/// <returns></returns> /// <returns></returns>
Task<Setting> GetSettingAsync(int settingId); Task<Setting> GetSettingAsync(string entityName, int settingId);
/// <summary> /// <summary>
@ -157,7 +186,7 @@ namespace Oqtane.Services
/// </summary> /// </summary>
/// <param name="settingId"></param> /// <param name="settingId"></param>
/// <returns></returns> /// <returns></returns>
Task DeleteSettingAsync(int settingId); Task DeleteSettingAsync(string entityName, int settingId);
/// <summary> /// <summary>
/// Gets the value of the given settingName (key) from the given key-value dictionary /// Gets the value of the given settingName (key) from the given key-value dictionary
@ -180,5 +209,13 @@ namespace Oqtane.Services
Dictionary<string, string> SetSetting(Dictionary<string, string> settings, string settingName, string settingValue, bool isPublic); Dictionary<string, string> SetSetting(Dictionary<string, string> settings, string settingName, string settingValue, bool isPublic);
Dictionary<string, string> MergeSettings(Dictionary<string, string> settings1, Dictionary<string, string> settings2); Dictionary<string, string> MergeSettings(Dictionary<string, string> settings1, Dictionary<string, string> settings2);
}
[Obsolete("GetSettingAsync(int settingId) is deprecated. Use GetSettingAsync(string entityName, int settingId) instead.", false)]
Task<Setting> GetSettingAsync(int settingId);
[Obsolete("DeleteSettingAsync(int settingId) is deprecated. Use DeleteSettingAsync(string entityName, int settingId) instead.", false)]
Task DeleteSettingAsync(int settingId);
}
} }

View File

@ -0,0 +1,48 @@
using Oqtane.Models;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Oqtane.Services
{
/// <summary>
/// Service to manage <see cref="UrlMapping"/>s on a <see cref="Site"/>
/// </summary>
public interface IUrlMappingService
{
/// <summary>
/// Get all <see cref="UrlMapping"/>s of this <see cref="Site"/>.
///
/// </summary>
/// <param name="siteId">ID-reference of a <see cref="Site"/></param>
/// <returns></returns>
Task<List<UrlMapping>> GetUrlMappingsAsync(int siteId, bool isMapped);
/// <summary>
/// Get one specific <see cref="UrlMapping"/>
/// </summary>
/// <param name="urlMappingId">ID-reference of a <see cref="UrlMapping"/></param>
/// <returns></returns>
Task<UrlMapping> GetUrlMappingAsync(int urlMappingId);
/// <summary>
/// Add / save a new <see cref="UrlMapping"/> to the database.
/// </summary>
/// <param name="urlMapping"></param>
/// <returns></returns>
Task<UrlMapping> AddUrlMappingAsync(UrlMapping urlMapping);
/// <summary>
/// Update a <see cref="UrlMapping"/> in the database.
/// </summary>
/// <param name="urlMapping"></param>
/// <returns></returns>
Task<UrlMapping> UpdateUrlMappingAsync(UrlMapping urlMapping);
/// <summary>
/// Delete a <see cref="UrlMapping"/> in the database.
/// </summary>
/// <param name="urlMappingId">ID-reference of a <see cref="UrlMapping"/></param>
/// <returns></returns>
Task DeleteUrlMappingAsync(int urlMappingId);
}
}

View File

@ -0,0 +1,29 @@
using Oqtane.Models;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Oqtane.Services
{
/// <summary>
/// Service to manage <see cref="Visitor"/>s on a <see cref="Site"/>
/// </summary>
public interface IVisitorService
{
/// <summary>
/// Get all <see cref="Visitor"/>s of this <see cref="Site"/>.
///
/// </summary>
/// <param name="siteId">ID-reference of a <see cref="Site"/></param>
/// <returns></returns>
Task<List<Visitor>> GetVisitorsAsync(int siteId, DateTime fromDate);
/// <summary>
/// Get a specific <see cref="Visitor"/> of this <see cref="Site"/>.
///
/// </summary>
/// <param name="visitorId">ID-reference of a <see cref="Visitor"/></param>
/// <returns></returns>
Task<Visitor> GetVisitorAsync(int visitorId);
}
}

View File

@ -21,6 +21,7 @@ namespace Oqtane.Services
} }
private string Apiurl => CreateApiUrl("Setting", _siteState.Alias); private string Apiurl => CreateApiUrl("Setting", _siteState.Alias);
public async Task<Dictionary<string, string>> GetTenantSettingsAsync() public async Task<Dictionary<string, string>> GetTenantSettingsAsync()
{ {
return await GetSettingsAsync(EntityNames.Tenant, -1); return await GetSettingsAsync(EntityNames.Tenant, -1);
@ -71,6 +72,16 @@ namespace Oqtane.Services
await UpdateSettingsAsync(moduleSettings, EntityNames.Module, moduleId); await UpdateSettingsAsync(moduleSettings, EntityNames.Module, moduleId);
} }
public async Task<Dictionary<string, string>> GetModuleDefinitionSettingsAsync(int moduleDefinitionId)
{
return await GetSettingsAsync(EntityNames.ModuleDefinition, moduleDefinitionId);
}
public async Task UpdateModuleDefinitionSettingsAsync(Dictionary<string, string> moduleDefinitionSettings, int moduleDefinitionId)
{
await UpdateSettingsAsync(moduleDefinitionSettings, EntityNames.ModuleDefinition, moduleDefinitionId);
}
public async Task<Dictionary<string, string>> GetUserSettingsAsync(int userId) public async Task<Dictionary<string, string>> GetUserSettingsAsync(int userId)
{ {
return await GetSettingsAsync(EntityNames.User, userId); return await GetSettingsAsync(EntityNames.User, userId);
@ -91,6 +102,16 @@ namespace Oqtane.Services
await UpdateSettingsAsync(folderSettings, EntityNames.Folder, folderId); await UpdateSettingsAsync(folderSettings, EntityNames.Folder, folderId);
} }
public async Task<Dictionary<string, string>> GetHostSettingsAsync()
{
return await GetSettingsAsync(EntityNames.Host, -1);
}
public async Task UpdateHostSettingsAsync(Dictionary<string, string> hostSettings)
{
await UpdateSettingsAsync(hostSettings, EntityNames.Host, -1);
}
public async Task<Dictionary<string, string>> GetSettingsAsync(string entityName, int entityId) public async Task<Dictionary<string, string>> GetSettingsAsync(string entityName, int entityId)
{ {
var dictionary = new Dictionary<string, string>(); var dictionary = new Dictionary<string, string>();
@ -144,9 +165,9 @@ namespace Oqtane.Services
} }
public async Task<Setting> GetSettingAsync(int settingId) public async Task<Setting> GetSettingAsync(string entityName, int settingId)
{ {
return await GetJsonAsync<Setting>($"{Apiurl}/{settingId}"); return await GetJsonAsync<Setting>($"{Apiurl}/{settingId}/{entityName}");
} }
public async Task<Setting> AddSettingAsync(Setting setting) public async Task<Setting> AddSettingAsync(Setting setting)
@ -159,9 +180,9 @@ namespace Oqtane.Services
return await PutJsonAsync<Setting>($"{Apiurl}/{setting.SettingId}", setting); return await PutJsonAsync<Setting>($"{Apiurl}/{setting.SettingId}", setting);
} }
public async Task DeleteSettingAsync(int settingId) public async Task DeleteSettingAsync(string entityName, int settingId)
{ {
await DeleteAsync($"{Apiurl}/{settingId}"); await DeleteAsync($"{Apiurl}/{settingId}/{entityName}");
} }
@ -220,5 +241,19 @@ namespace Oqtane.Services
} }
return settings1; return settings1;
} }
[Obsolete("GetSettingAsync(int settingId) is deprecated. Use GetSettingAsync(string entityName, int settingId) instead.", false)]
public async Task<Setting> GetSettingAsync(int settingId)
{
return await GetJsonAsync<Setting>($"{Apiurl}/{settingId}/tenant");
}
[Obsolete("DeleteSettingAsync(int settingId) is deprecated. Use DeleteSettingAsync(string entityName, int settingId) instead.", false)]
public async Task DeleteSettingAsync(int settingId)
{
await DeleteAsync($"{Apiurl}/{settingId}/tenant");
}
} }
} }

View File

@ -0,0 +1,51 @@
using Oqtane.Models;
using System.Threading.Tasks;
using System.Net.Http;
using System.Linq;
using System.Collections.Generic;
using Oqtane.Documentation;
using Oqtane.Shared;
namespace Oqtane.Services
{
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
public class UrlMappingService : ServiceBase, IUrlMappingService
{
private readonly SiteState _siteState;
public UrlMappingService(HttpClient http, SiteState siteState) : base(http)
{
_siteState = siteState;
}
private string Apiurl => CreateApiUrl("UrlMapping", _siteState.Alias);
public async Task<List<UrlMapping>> GetUrlMappingsAsync(int siteId, bool isMapped)
{
List<UrlMapping> urlMappings = await GetJsonAsync<List<UrlMapping>>($"{Apiurl}?siteid={siteId}&ismapped={isMapped}");
return urlMappings.OrderByDescending(item => item.RequestedOn).ToList();
}
public async Task<UrlMapping> GetUrlMappingAsync(int urlMappingId)
{
return await GetJsonAsync<UrlMapping>($"{Apiurl}/{urlMappingId}");
}
public async Task<UrlMapping> AddUrlMappingAsync(UrlMapping role)
{
return await PostJsonAsync<UrlMapping>(Apiurl, role);
}
public async Task<UrlMapping> UpdateUrlMappingAsync(UrlMapping role)
{
return await PutJsonAsync<UrlMapping>($"{Apiurl}/{role.UrlMappingId}", role);
}
public async Task DeleteUrlMappingAsync(int urlMappingId)
{
await DeleteAsync($"{Apiurl}/{urlMappingId}");
}
}
}

View File

@ -0,0 +1,37 @@
using Oqtane.Models;
using System.Threading.Tasks;
using System.Net.Http;
using System.Linq;
using System.Collections.Generic;
using Oqtane.Documentation;
using Oqtane.Shared;
using System;
namespace Oqtane.Services
{
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
public class VisitorService : ServiceBase, IVisitorService
{
private readonly SiteState _siteState;
public VisitorService(HttpClient http, SiteState siteState) : base(http)
{
_siteState = siteState;
}
private string Apiurl => CreateApiUrl("Visitor", _siteState.Alias);
public async Task<List<Visitor>> GetVisitorsAsync(int siteId, DateTime fromDate)
{
List<Visitor> visitors = await GetJsonAsync<List<Visitor>>($"{Apiurl}?siteid={siteId}&fromdate={fromDate.ToString("dd-MMM-yyyy")}");
return visitors.OrderByDescending(item => item.VisitedOn).ToList();
}
public async Task<Visitor> GetVisitorAsync(int visitorId)
{
return await GetJsonAsync<Visitor>($"{Apiurl}/{visitorId}");
}
}
}

View File

@ -1,7 +1,7 @@
@namespace Oqtane.Themes.BlazorTheme @namespace Oqtane.Themes.BlazorTheme
@inherits ContainerBase @inherits ContainerBase
<div class="container"> <div class="container">
<div class="row px-4"> <div class="row p-4">
<div class="d-flex flex-nowrap"> <div class="d-flex flex-nowrap">
<ModuleActions /><h2><ModuleTitle /></h2> <ModuleActions /><h2><ModuleTitle /></h2>
</div> </div>

View File

@ -1,7 +1,9 @@
using Oqtane.Models; using Oqtane.Documentation;
using Oqtane.Models;
namespace Oqtane.Themes.BlazorTheme namespace Oqtane.Themes.BlazorTheme
{ {
[PrivateApi("Mark Build-In Theme-Info classes as private, since it's not very useful in the public docs")]
public class ThemeInfo : ITheme public class ThemeInfo : ITheme
{ {
public Theme Theme => new Theme public Theme Theme => new Theme

View File

@ -1,23 +1,26 @@
@namespace Oqtane.Themes.BlazorTheme @namespace Oqtane.Themes.BlazorTheme
@inherits ThemeBase @inherits ThemeBase
<div class="breadcrumbs"> <div class="breadcrumbs">
<Breadcrumbs /> <Breadcrumbs />
</div> </div>
<div class="sidebar"> <div class="row flex-xl-nowrap gx-0">
<nav class="navbar"> <div class="sidebar">
<Logo /><Menu Orientation="Vertical" /> <nav class="navbar">
</nav> <Logo /><Menu Orientation="Vertical" />
</div> </nav>
<div class="main">
<div class="top-row px-4">
<div class="ms-auto"><UserProfile /> <Login /> <ControlPanel /></div>
</div> </div>
<div class="container">
<div class="row px-4"> <div class="main g-0">
<Pane Name="@PaneNames.Admin" /> <div class="top-row px-4">
<div class="ms-auto"><UserProfile /> <Login /> <ControlPanel /></div>
</div>
<div class="container">
<div class="row px-4">
<Pane Name="@PaneNames.Admin" />
</div>
</div> </div>
</div> </div>
</div> </div>
@ -27,9 +30,10 @@
public override List<Resource> Resources => new List<Resource>() public override List<Resource> Resources => new List<Resource>()
{ {
new Resource { ResourceType = ResourceType.Stylesheet, Url = "https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css", Integrity = "sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC", CrossOrigin = "anonymous" }, // obtained from https://cdnjs.com/libraries
new Resource { ResourceType = ResourceType.Stylesheet, Url = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.0.2/css/bootstrap.min.css", Integrity = "sha512-usVBAd66/NpVNfBge19gws2j6JZinnca12rAe2l+d+QkLU9fiG02O1X8Q6hepIpr/EYKZvKx/I9WsnujJuOmBA==", CrossOrigin = "anonymous" },
new Resource { ResourceType = ResourceType.Stylesheet, Url = ThemePath() + "Theme.css" }, new Resource { ResourceType = ResourceType.Stylesheet, Url = ThemePath() + "Theme.css" },
new Resource { ResourceType = ResourceType.Script, Bundle = "Bootstrap", Url = "https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js", Integrity = "sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM", CrossOrigin = "anonymous" } new Resource { ResourceType = ResourceType.Script, Bundle = "Bootstrap", Url = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.1.3/js/bootstrap.bundle.min.js", Integrity = "sha512-pax4MlgXjHEPfCwcJLQhigY7+N8rt6bVvWLFyUMuxShv170X53TRzGPmPkZmGBhk+jikR8WBM4yl7A9WMHHqvg==", CrossOrigin = "anonymous" }
}; };
} }

View File

@ -16,7 +16,15 @@
else else
{ {
<li class="breadcrumb-item"> <li class="breadcrumb-item">
<a href="@NavigateUrl(p.Path)">@p.Name</a> @if(p.IsClickable)
{
<a href="@NavigateUrl(p.Path)">@p.Name</a>
}
else
{
@p.Name
}
</li> </li>
} }
} }

View File

@ -12,183 +12,6 @@
@inject IStringLocalizer<ControlPanel> Localizer @inject IStringLocalizer<ControlPanel> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer @inject IStringLocalizer<SharedResources> SharedLocalizer
@if (_moduleDefinitions != null && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions))
{
<div class="@ContainerClass" tabindex="-1" data-bs-scroll="true" data-bs-backdrop="true" id="offcanvasControlPanel" aria-labelledby="offcanvasScrollingLabel">
<div class="@HeaderClass">
<h5 id="offcanvasScrollingLabel">@Localizer["ControlPanel"]</h5>
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" aria-label="Close"></button>
</div>
<div class="@BodyClass">
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
{
<div class="row d-flex">
<div class="col">
<button data-bs-dismiss="offcanvas" type="button" class="btn btn-primary col-12" @onclick=@(async () => Navigate("Admin"))>@Localizer["AdminDash"]</button>
</div>
</div>
<hr class="app-rule" />
<div class="row">
<div class="col text-center">
<label class="control-label">@Localizer["Page.Manage"] </label>
</div>
</div>
<div class="row d-flex">
<div class="col d-flex justify-content-between">
<button type="button" class="btn btn-secondary col-3" data-bs-dismiss="offcanvas" @onclick=@(async () => Navigate("Add"))>@SharedLocalizer["Add"]</button>
<button type="button" class="btn btn-secondary col-3" data-bs-dismiss="offcanvas" @onclick=@(async () => Navigate("Edit"))>@SharedLocalizer["Edit"]</button>
<button type="button" class="btn btn-danger col-3" @onclick="ConfirmDelete">@SharedLocalizer["Delete"]</button>
</div>
</div>
<br />
<div class="row d-flex">
<div class="col">
@if (UserSecurity.GetPermissionStrings(PageState.Page.Permissions).FirstOrDefault(item => item.PermissionName == PermissionNames.View).Permissions.Split(';').Contains(RoleNames.Everyone))
{
<button type="button" class="btn btn-secondary col-12" @onclick=@(async () => Publish("unpublish"))>@Localizer["Page.Unpublish"]</button>
}
else
{
<button type="button" class="btn btn-secondary col-12" @onclick=@(async () => Publish("publish"))>@Localizer["Page.Publish"]</button>
}
</div>
</div>
}
@if (_deleteConfirmation)
{
<div class="app-admin-modal">
<div class="modal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">@Localizer["Page.Delete"]</h5>
<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>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger" @onclick="DeletePage">@SharedLocalizer["Delete"]</button>
<button type="button" class="btn btn-secondary" @onclick="ConfirmDelete">@SharedLocalizer["Cancel"]</button>
</div>
</div>
</div>
</div>
</div>
}
<hr class="app-rule" />
<div class="row">
<div class="col text-center">
<label for="Module" class="control-label">@Localizer["Module.Manage"] </label>
<select class="form-select" @bind="@ModuleType">
<option value="new">@Localizer["Module.AddNew"]</option>
<option value="existing">@Localizer["Module.AddExisting"]</option>
</select>
@if (ModuleType == "new")
{
@if (_moduleDefinitions != null)
{
<select class="form-select" @onchange="(e => CategoryChanged(e))">
@foreach (var category in _categories)
{
if (category == Category)
{
<option value="@category" selected>@category @Localizer["Modules"]</option>
}
else
{
<option value="@category">@category @Localizer["Modules"]</option>
}
}
</select>
<select class="form-select" @onchange="(e => ModuleChanged(e))">
@if (ModuleDefinitionName == "-")
{
<option value="-" selected>&lt;@Localizer["Module.Select"]&gt;</option>
}
else
{
<option value="-">&lt;@Localizer["Module.Select"]&gt;</option>
}
@foreach (var moduledefinition in _moduleDefinitions)
{
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Utilize, moduledefinition.Permissions))
{
if (moduledefinition.Runtimes == "" || moduledefinition.Runtimes.Contains(PageState.Runtime.ToString()))
{
<option value="@moduledefinition.ModuleDefinitionName">@moduledefinition.Name</option>
}
}
}
</select>
@((MarkupString) Description)
}
}
else
{
<select class="form-select" @onchange="(e => PageChanged(e))">
<option value="-">&lt;@Localizer["Page.Select"]&gt;</option>
@foreach (Page p in _pages)
{
<option value="@p.PageId">@p.Name</option>
}
</select>
<select class="form-select" @bind="@ModuleId">
<option value="-">&lt;@Localizer["Module.Select"]&gt;</option>
@foreach (Module module in _modules)
{
<option value="@module.ModuleId">@module.Title</option>
}
</select>
}
</div>
</div>
<div class="row">
<div class="col text-center">
<label for="Title" class="control-label">@Localizer["Title"] </label>
<input type="text" name="Title" class="form-control" @bind="@Title" />
</div>
</div>
@if (_pane.Length > 1)
{
<div class="row">
<div class="col text-center">
<label for="Pane" class="control-label">@Localizer["Pane"] </label>
<select class="form-select" @bind="@Pane">
@foreach (string pane in PageState.Page.Panes)
{
<option value="@pane">@pane Pane</option>
}
</select>
</div>
</div>
}
<div class="row">
<div class="col text-center">
<label for="Container" class="control-label">@Localizer["Container"] </label>
<select class="form-select" @bind="@ContainerType">
@foreach (var container in _containers)
{
<option value="@container.TypeName">@container.Name</option>
}
</select>
</div>
</div>
<br />
<button type="button" class="btn btn-primary col-12" @onclick="@AddModule">@Localizer["Page.Module.Add"]</button>
@((MarkupString) Message)
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
{
<hr class="app-rule" />
<NavLink class="btn btn-info col-12" data-bs-dismiss="offcanvas" href="@NavigateUrl("admin/update")">@Localizer["System.Update"]</NavLink>
}
</div>
</div>
}
@if (ShowLanguageSwitcher) @if (ShowLanguageSwitcher)
{ {
<LanguageSwitcher /> <LanguageSwitcher />
@ -212,10 +35,185 @@
@if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions)) @if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions))
{ {
<button type="button" class="btn @ButtonClass" data-bs-toggle="offcanvas" data-bs-target="#offcanvasControlPanel" aria-controls="offcanvasControlPanel">
<button class="btn @ButtonClass" type="button" data-bs-toggle="offcanvas" data-bs-target="#offcanvasControlPanel" aria-controls="offcanvasControlPanel">
<span class="oi oi-cog"></span> <span class="oi oi-cog"></span>
</button> </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>
</div>
<div class="@BodyClass">
<div class="container-fluid">
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
{
<div class="row d-flex">
<div class="col">
<button type="button" data-bs-dismiss="offcanvas" class="btn btn-primary col-12" @onclick=@(async () => Navigate("Admin"))>@Localizer["AdminDash"]</button>
</div>
</div>
<hr class="app-rule" />
<div class="row">
<div class="col text-center">
<label class="control-label">@Localizer["Page.Manage"] </label>
</div>
</div>
<div class="row d-flex">
<div class="col d-flex justify-content-between">
<button type="button" class="btn btn-secondary col me-1" data-bs-dismiss="offcanvas" @onclick=@(async () => Navigate("Add"))>@SharedLocalizer["Add"]</button>
<button type="button" class="btn btn-secondary col" data-bs-dismiss="offcanvas" @onclick=@(async () => Navigate("Edit"))>@SharedLocalizer["Edit"]</button>
<button type="button" class="btn btn-danger col ms-1" @onclick="ConfirmDelete">@SharedLocalizer["Delete"]</button>
</div>
</div>
<br />
<div class="row d-flex">
<div class="col">
@if (UserSecurity.GetPermissionStrings(PageState.Page.Permissions).FirstOrDefault(item => item.PermissionName == PermissionNames.View).Permissions.Split(';').Contains(RoleNames.Everyone))
{
<button type="button" class="btn btn-secondary col-12" @onclick=@(async () => Publish("unpublish"))>@Localizer["Page.Unpublish"]</button>
}
else
{
<button type="button" class="btn btn-secondary col-12" @onclick=@(async () => Publish("publish"))>@Localizer["Page.Publish"]</button>
}
</div>
</div>
}
@if (_deleteConfirmation)
{
<div class="app-admin-modal">
<div class="modal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">@Localizer["Page.Delete"]</h5>
<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>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger" @onclick="DeletePage">@SharedLocalizer["Delete"]</button>
<button type="button" class="btn btn-secondary" @onclick="ConfirmDelete">@SharedLocalizer["Cancel"]</button>
</div>
</div>
</div>
</div>
</div>
}
<hr class="app-rule" />
<div class="row">
<div class="col text-center">
<label for="Module" class="control-label">@Localizer["Module.Manage"] </label>
<select class="form-select" @bind="@ModuleType">
<option value="new">@Localizer["Module.AddNew"]</option>
<option value="existing">@Localizer["Module.AddExisting"]</option>
</select>
@if (ModuleType == "new")
{
@if (_moduleDefinitions != null)
{
<select class="form-select" @onchange="(e => CategoryChanged(e))">
@foreach (var category in _categories)
{
if (category == Category)
{
<option value="@category" selected>@category @Localizer["Modules"]</option>
}
else
{
<option value="@category">@category @Localizer["Modules"]</option>
}
}
</select>
<select class="form-select" @onchange="(e => ModuleChanged(e))">
@if (ModuleDefinitionName == "-")
{
<option value="-" selected>&lt;@Localizer["Module.Select"]&gt;</option>
}
else
{
<option value="-">&lt;@Localizer["Module.Select"]&gt;</option>
}
@foreach (var moduledefinition in _moduleDefinitions)
{
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Utilize, moduledefinition.Permissions))
{
if (moduledefinition.Runtimes == "" || moduledefinition.Runtimes.Contains(PageState.Runtime.ToString()))
{
<option value="@moduledefinition.ModuleDefinitionName">@moduledefinition.Name</option>
}
}
}
</select>
@((MarkupString) Description)
}
}
else
{
<select class="form-select" @onchange="(e => PageChanged(e))">
<option value="-">&lt;@Localizer["Page.Select"]&gt;</option>
@foreach (Page p in _pages)
{
<option value="@p.PageId">@p.Name</option>
}
</select>
<select class="form-select" @bind="@ModuleId">
<option value="-">&lt;@Localizer["Module.Select"]&gt;</option>
@foreach (Module module in _modules)
{
<option value="@module.ModuleId">@module.Title</option>
}
</select>
}
</div>
</div>
<div class="row">
<div class="col text-center">
<label for="Title" class="control-label">@Localizer["Title"] </label>
<input type="text" name="Title" class="form-control" @bind="@Title" />
</div>
</div>
@if (_pane.Length > 1)
{
<div class="row">
<div class="col text-center">
<label for="Pane" class="control-label">@Localizer["Pane"] </label>
<select class="form-select" @bind="@Pane">
@foreach (string pane in PageState.Page.Panes)
{
<option value="@pane">@pane Pane</option>
}
</select>
</div>
</div>
}
<div class="row">
<div class="col text-center">
<label for="Container" class="control-label">@Localizer["Container"] </label>
<select class="form-select" @bind="@ContainerType">
@foreach (var container in _containers)
{
<option value="@container.TypeName">@container.Name</option>
}
</select>
</div>
</div>
<br />
<button type="button" class="btn btn-primary col-12" @onclick="@AddModule">@Localizer["Page.Module.Add"]</button>
@((MarkupString) Message)
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
{
<hr class="app-rule" />
<NavLink class="btn btn-info col-12" data-bs-dismiss="offcanvas" href="@NavigateUrl("admin/update")">@Localizer["System.Update"]</NavLink>
}
</div>
</div>
</div>
} }
@code{ @code{

View File

@ -23,5 +23,5 @@
@code @code
{ {
[Parameter] [Parameter]
public bool ShowLogin { get; set; } public bool ShowLogin { get; set; } = true;
} }

View File

@ -4,12 +4,12 @@
@if (MenuPages.Any()) @if (MenuPages.Any())
{ {
<span class="app-menu-toggler"> <span class="app-menu-toggler navbar-expand-md">
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#Menu" aria-controls="Menu" aria-expanded="false" aria-label="Toggle Navigation"> <button type="button" class="navbar-toggler" data-bs-toggle="collapse" data-bs-target="#Menu" aria-controls="Menu" aria-expanded="false" aria-label="Toggle Navigation">
<span class="navbar-toggler-icon"></span> <span class="navbar-toggler-icon"></span>
</button> </button>
</span> </span>
<div class="app-menu"> <div class="app-menu navbar-expand-md">
<div class="collapse navbar-collapse" id="Menu"> <div class="collapse navbar-collapse" id="Menu">
<MenuItemsHorizontal ParentPage="null" Pages="MenuPages" /> <MenuItemsHorizontal ParentPage="null" Pages="MenuPages" />
</div> </div>

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