Compare commits

..

333 Commits

Author SHA1 Message Date
3259494d45 Merge pull request #5593 from oqtane/master
6.2.0 Release
2025-09-09 16:24:37 -04:00
0a03eb620a Merge pull request #5592 from oqtane/dev
6.2.0 Release
2025-09-09 16:24:12 -04:00
701d8c9a57 Update README.md 2025-09-09 16:23:07 -04:00
fdca8a2890 Merge pull request #5591 from sbwalker/dev
ensure Radzen.Blazor static assets are included in publish output
2025-09-09 14:25:08 -04:00
22e2a4da1e ensure Radzen.Blazor static assets are included in publish output 2025-09-09 14:24:53 -04:00
409523912b Merge pull request #5590 from sbwalker/dev
use standard port #'s
2025-09-09 13:31:08 -04:00
d5c68444c3 use standard port #'s 2025-09-09 13:30:53 -04:00
ffa93e0ee7 Merge pull request #5589 from sbwalker/dev
profile improvements
2025-09-09 08:52:36 -04:00
3f4f1a8278 profile improvements 2025-09-09 08:52:20 -04:00
8e70949880 Merge pull request #5588 from sbwalker/dev
improve setting import
2025-09-08 12:55:58 -04:00
be8436d237 improve setting import 2025-09-08 12:55:45 -04:00
876f13be5e Merge pull request #5587 from sbwalker/dev
add setting import
2025-09-08 12:13:34 -04:00
dfca6640da add setting import 2025-09-08 12:13:17 -04:00
a2e57bc54c Merge pull request #5585 from sbwalker/dev
improve validation
2025-09-05 17:38:17 -04:00
dcc2e59e46 improve validation 2025-09-05 17:38:02 -04:00
90e721b172 Merge pull request #5584 from sbwalker/dev
added a Setting Management UI
2025-09-05 17:20:50 -04:00
94391875d5 added a Setting Management UI 2025-09-05 17:20:31 -04:00
43d06c042d Merge pull request #5583 from sbwalker/dev
allow installer logo to be overridden
2025-09-05 13:12:56 -04:00
3e12910fbd allow installer logo to be overridden 2025-09-05 13:12:39 -04:00
ba70ebe23c Merge pull request #5582 from sbwalker/dev
update application template
2025-09-05 12:36:18 -04:00
b739841495 update application template 2025-09-05 12:36:03 -04:00
acabc75aa6 Merge pull request #5581 from sbwalker/dev
restructure text editors and static assets
2025-09-05 12:33:00 -04:00
27041f464f restructure text editors and static assets 2025-09-05 12:32:43 -04:00
9f923ae968 Merge pull request #5577 from zyhfish/task/radzen-text-editor
implement radzen text editor.
2025-09-05 11:13:52 -04:00
9c7d832357 Merge pull request #5580 from sbwalker/dev
exception handling needs to encapsulate entire method
2025-09-05 11:13:37 -04:00
e913c10d5b exception handling needs to encapsulate entire method 2025-09-05 11:13:21 -04:00
c698188901 Merge pull request #5576 from W6HBR/dev
fix job status issue for disabled jobs
2025-09-05 11:07:52 -04:00
8fd67621ac Merge pull request #5579 from sbwalker/dev
update dependencies in Oqtane.Server.nusepc
2025-09-05 11:05:47 -04:00
0c60085e09 update dependencies in Oqtane.Server.nusepc 2025-09-05 11:05:18 -04:00
1826316c80 Merge pull request #5567 from thabaum/Update-v6.2.0-azure-deploy-and-dependencies
Update v6.2.0 azure deploy and Oqtane.Server project dependencies
2025-09-05 11:02:45 -04:00
07341aeebe Fix Azure deployment link in README
Updated Azure deployment link to use the master branch.
2025-09-05 11:02:00 -04:00
9f6945dda2 Merge pull request #5578 from oqtane/master
Update azuredeploy.json
2025-09-05 11:01:15 -04:00
b39b568b4c Update azuredeploy.json 2025-09-05 11:00:06 -04:00
Ben
e59d5fd339 implement radzen text editor. 2025-09-05 20:36:50 +08:00
b7bc527d6c Added resource message for Message.Job.Disabled 2025-09-04 21:52:24 -07:00
1ea76d06d1 Change StartJob to check if job is enabled 2025-09-04 21:47:51 -07:00
b049be9d83 Change IsStarted to follow IsEnabled value upon startup.
Changes behavior of IsStarted to follow the same value of IsEnabled.
2025-09-04 21:44:14 -07:00
966fc55594 Merge pull request #5574 from sbwalker/dev
fix #5570 - multi-database installation authentication issue
2025-09-04 14:02:24 -04:00
ca9ddbd90f fix #5570 - multi-database installation authentication issue 2025-09-04 14:01:42 -04:00
0d04926d9f Merge pull request #5569 from sbwalker/dev
fix issue in application template
2025-09-02 17:11:32 -04:00
2b500d41ca fix issue in application template 2025-09-02 17:11:23 -04:00
5c67eeea58 Merge pull request #5568 from sbwalker/dev
fix issue in default module template
2025-09-02 17:10:06 -04:00
09daf3f6cc fix issue in default module template 2025-09-02 17:09:55 -04:00
9a06a3311e Update azuredeploy.json to v6.2.0 2025-09-02 12:58:04 -07:00
304694fbf9 Update to latest SQLitePCLRaw.bundle_e_sqlite3 & Swashbuckle.AspNetCore Package Dependencies 2025-09-02 12:53:42 -07:00
96ba42df96 Merge pull request #5565 from sbwalker/dev
bump version to 6.2.0
2025-09-02 13:59:38 -04:00
e7bc11d026 bump version to 6.2.0 2025-09-02 13:59:27 -04:00
1272305355 Merge pull request #5564 from sbwalker/dev
fix help text related to module/theme upload
2025-09-02 08:50:45 -04:00
30c6da13c2 fix help text related to module/theme upload 2025-09-02 08:49:32 -04:00
5aacb2b877 Merge pull request #5563 from sbwalker/dev
allow modules to be able to specify the databases they support
2025-09-02 08:33:51 -04:00
b5fdf42c37 allow modules to be able to specify the databases they support 2025-09-02 08:32:46 -04:00
c81d677c5c Merge pull request #5559 from sbwalker/dev
added support for cookie domain option in User Management Settings
2025-08-30 08:01:56 -04:00
6daf675e52 added support for cookie domain option in User Management Settings 2025-08-30 08:01:18 -04:00
3f7a7f3340 Merge pull request #5558 from sbwalker/dev
added StaticAssetPath properties to base classes
2025-08-30 07:49:06 -04:00
1ebf3c4077 added StaticAssetPath properties to base classes 2025-08-30 07:48:26 -04:00
1f1173ae03 Merge pull request #5557 from sbwalker/dev
add comments
2025-08-30 07:27:23 -04:00
efa466e1d6 add comments 2025-08-30 07:26:37 -04:00
cefe349b4e Merge pull request #5555 from sbwalker/dev
remove hardcoded references to LocalDB
2025-08-29 17:17:27 -04:00
a9bc356f37 remove hardcoded references to LocalDB 2025-08-29 17:16:42 -04:00
6fc791020c Merge pull request #5554 from sbwalker/dev
move default template static assets
2025-08-29 16:34:35 -04:00
713ec1b373 move default template static assets 2025-08-29 16:33:51 -04:00
e3fa781122 Merge pull request #5553 from sbwalker/dev
improve default theme template to follow RCL/Nuget standards
2025-08-29 16:31:18 -04:00
e4b6d0ff29 improve default theme template to follow RCL/Nuget standards 2025-08-29 16:30:49 -04:00
cd2a328560 Merge pull request #5552 from sbwalker/dev
improve default module template to follow RCL/Nuget standards
2025-08-29 16:21:06 -04:00
d2d88d4b5e improve default module template to follow RCL/Nuget standards 2025-08-29 16:20:16 -04:00
0067cc4266 Added FixProps command line utility 2025-08-29 15:19:07 -04:00
da3afefa8d Merge pull request #5551 from sbwalker/dev
update default module/theme templates to use projectType rather than dependency in nuspec file
2025-08-29 15:15:12 -04:00
ab534d07f3 update default module/theme templates to use projectType rather than dependency in nuspec file 2025-08-29 15:14:48 -04:00
49c513ac9b Merge pull request #5550 from sbwalker/dev
add support for packageType in nuspec files for minimum Oqtane version
2025-08-29 14:31:07 -04:00
6f7a18674e add support for packageType in nuspec files for minimum Oqtane version 2025-08-29 14:30:49 -04:00
0f559ba42d Merge pull request #5546 from sbwalker/dev
install wizard should use RenderMode and Runtime values from appsettings.json when creating site
2025-08-27 14:28:43 -04:00
2af02fae95 install wizard should use RenderMode and Runtime values from appsettings.json when creating site 2025-08-27 14:28:23 -04:00
006423e32e Change RenderMode from Interactive to Static 2025-08-27 14:27:28 -04:00
23f29ca55d Change RenderMode from Interactive to Static 2025-08-27 14:27:01 -04:00
68a7571741 Change RenderMode from Interactive to Static 2025-08-27 14:26:47 -04:00
10e60e352a Merge pull request #5545 from sbwalker/dev
improve help text
2025-08-27 14:08:08 -04:00
3b16ae8cc0 improve help text 2025-08-27 14:07:51 -04:00
66c4737021 Merge pull request #5533 from zyhfish/task/fix-5532
Fix #5532: add require nonce setting.
2025-08-27 13:55:20 -04:00
8684e03af1 Merge pull request #5544 from sbwalker/dev
fix #5531 - external login single sign-on for multiple sites
2025-08-27 13:54:46 -04:00
edad9e6b3c fix #5531 - external login single sign-on for multiple sites 2025-08-27 13:54:30 -04:00
66b89752d3 Merge pull request #5543 from sbwalker/dev
fix resources in default theme template
2025-08-27 12:29:04 -04:00
9a6195edf1 fix resources in default theme template 2025-08-27 12:28:51 -04:00
2bd07b54b6 Merge pull request #5542 from sbwalker/dev
optimize client startup in templates
2025-08-27 12:21:16 -04:00
7cf9d9ad65 optimize client startup in templates 2025-08-27 12:20:59 -04:00
4dff30ec8c Merge pull request #5540 from sbwalker/dev
default index component to interactive
2025-08-27 09:21:34 -04:00
581f14e661 default index component to interactive 2025-08-27 09:21:19 -04:00
8ccdc37b64 Merge pull request #5538 from sbwalker/dev
fix naming
2025-08-26 17:22:23 -04:00
9e85b35498 fix naming 2025-08-26 17:22:10 -04:00
fff408a5bf Merge pull request #5537 from sbwalker/dev
application template changes
2025-08-26 17:16:01 -04:00
4d5168c998 application template changes 2025-08-26 17:15:46 -04:00
bf2c978f1d Merge pull request #5536 from sbwalker/dev
optimize startup
2025-08-26 15:27:51 -04:00
ec06c1cdf1 optimize startup 2025-08-26 15:27:35 -04:00
Ben
f451cfce09 Fix #5532: remove duplicated semi colon. 2025-08-26 20:27:41 +08:00
Ben
91e55aeb9b Fix #5532: change the default value to true. 2025-08-26 20:26:11 +08:00
Ben
919fb5012f Fix #5532: add require nonce setting. 2025-08-26 18:13:09 +08:00
2bb6226e78 Merge pull request #5530 from sbwalker/dev
remove unecessary content exclusion
2025-08-22 14:25:59 -04:00
6a0c47f7b1 remove unecessary content exclusion 2025-08-22 14:21:49 -04:00
31b688cbf6 Merge pull request #5529 from sbwalker/dev
make kestrel the default web server for the app template
2025-08-22 10:06:25 -04:00
7f1fed2fb1 make kestrel the default web server for the app template 2025-08-22 10:06:10 -04:00
aa6c876b12 Merge pull request #5528 from sbwalker/dev
more template optimizations
2025-08-22 09:17:40 -04:00
4e33aeef89 more template optimizations 2025-08-22 09:17:24 -04:00
e2601dcf05 Merge pull request #5527 from sbwalker/dev
increment template version
2025-08-22 08:55:37 -04:00
247baa375d increment template version 2025-08-22 08:55:20 -04:00
a4adba846e Merge pull request #5526 from sbwalker/dev
declare dependencies in nuspec files and optimize application template
2025-08-22 08:42:09 -04:00
52799c7cb0 declare dependencies in nuspec files and optimize application template 2025-08-22 08:41:52 -04:00
a8635dc555 Merge pull request #5523 from sbwalker/dev
fix #5520 - site soft delete should only be visible to Host users
2025-08-21 09:59:31 -04:00
cca0f2219e fix #5520 - site soft delete should only be visible to Host users 2025-08-21 09:59:15 -04:00
d2f8c3c2bb Merge pull request #5522 from sbwalker/dev
fix #5519 - Site hard delete exception
2025-08-21 09:57:44 -04:00
0f38df053f fix #5519 - Site hard delete exception 2025-08-21 09:57:29 -04:00
5c926a10a7 Merge pull request #5521 from sbwalker/dev
template updates
2025-08-21 09:56:18 -04:00
036bbb418e template updates 2025-08-21 09:56:03 -04:00
93d224fa37 Merge pull request #5518 from sbwalker/dev
support for staticwebassets folder in Nuget packages
2025-08-20 07:09:50 -04:00
5b45e3e417 support for staticwebassets folder in Nuget packages 2025-08-20 07:09:33 -04:00
c2f2dfd837 Merge pull request #5517 from sbwalker/dev
fix filename in template
2025-08-19 14:01:38 -04:00
2f2baf12fb fix filename in template 2025-08-19 14:01:25 -04:00
052c339d0d Merge pull request #5516 from sbwalker/dev
add additional SSL connection options for SMTP
2025-08-19 13:16:51 -04:00
96192e2e06 add additional SSL connection options for SMTP 2025-08-19 13:16:37 -04:00
ea9fa30358 Merge pull request #5515 from sbwalker/dev
template improvements
2025-08-19 12:27:10 -04:00
78f8e2f484 template improvements 2025-08-19 12:26:54 -04:00
0fe2a3fb80 Merge pull request #5514 from sbwalker/dev
add project reference in AppHost to Server so that dependencies will be automatically copied
2025-08-19 12:10:35 -04:00
a340f52973 add project reference in AppHost to Server so that dependencies will be automatically copied 2025-08-19 12:10:15 -04:00
bd94b715ba Merge pull request #5513 from sbwalker/dev
Resources for Interactive module components should be managed via JS Interop
2025-08-19 12:05:58 -04:00
b9a97ffa4c Resources for Interactive module components should be managed via JS Interop 2025-08-19 12:05:39 -04:00
5a37ab1b89 Merge pull request #5510 from sbwalker/dev
modify template content
2025-08-17 10:54:01 -04:00
67a6ac2240 Merge branch 'dev' into dev 2025-08-17 10:53:54 -04:00
7b42845ecc modify template content 2025-08-17 10:53:22 -04:00
3ef39896d1 Merge pull request #5509 from sbwalker/dev
update Azure ARM template to 6.1.5
2025-08-17 10:29:37 -04:00
b01f3b505d update Azure ARM template to 6.1.5 2025-08-17 10:29:25 -04:00
84c5e4c30b Update README.md 2025-08-17 10:10:56 -04:00
abc0f3943e Merge pull request #5507 from oqtane/master
6.1.5 Release
2025-08-17 09:59:14 -04:00
c7b71db015 Merge pull request #5506 from oqtane/dev
6.1.5 Release
2025-08-17 09:58:54 -04:00
f5a8a953bb Update README.md 2025-08-16 09:39:09 -04:00
8e965912aa Update README.md 2025-08-16 09:34:21 -04:00
6c3cfb0c7a Update README.md 2025-08-16 09:33:28 -04:00
85d162aa9d Update README.md 2025-08-16 09:32:50 -04:00
67c460dfa5 Merge pull request #5502 from thabaum/6.1.5-Maui-Version-9.0.100
Fixes #5501: Updates Maui Project Dependencies to version 9.0.100
2025-08-15 16:15:49 -04:00
83d35dbc65 Updates Maui Project Dependencies to version 9.0.100 2025-08-15 11:19:00 -07:00
86735a5afd Update README.md 2025-08-15 14:15:13 -04:00
6ecbb89469 Merge pull request #5500 from sbwalker/dev
consolidate packaging
2025-08-15 14:07:07 -04:00
2ca0508030 consolidate packaging 2025-08-15 14:06:28 -04:00
8fbd50dcef Merge pull request #5499 from sbwalker/dev
resolve issue related to moving database providers to Oqtane.Server
2025-08-15 13:57:16 -04:00
2143660345 resolve issue related tp moving database providers to Oqtane.Server 2025-08-15 13:56:57 -04:00
8c903fbfdd Merge pull request #5496 from sbwalker/dev
update Microsoft.Data.SqlClient in AppHost
2025-08-15 13:37:49 -04:00
33be372348 update Microsoft.Data.SqlClient in AppHost 2025-08-15 13:37:35 -04:00
447ec3f5e6 Merge pull request #5495 from leigh-pointer/Microsoft.Data.SqlClient
Microsoft.Data.SqlClient updated
2025-08-15 13:35:00 -04:00
a4aed69887 Microsoft.Data.SqlClient updated
Updated Microsoft.Data.SqlClient to 6.1.1
2025-08-15 19:01:03 +02:00
bbbd6e9e3e Merge pull request #5494 from sbwalker/dev
remove unecessary using
2025-08-15 12:46:49 -04:00
06712faee9 remove unecessary using 2025-08-15 12:46:35 -04:00
48a90072ee Update README.md 2025-08-15 12:44:50 -04:00
0344f4d60b Update README.md 2025-08-15 12:44:31 -04:00
6a4affd5a6 Merge pull request #5493 from sbwalker/dev
add a new Visual Studio Project Template
2025-08-15 12:43:53 -04:00
d73e2288bb add a new Visual Studio Project Template 2025-08-15 12:43:32 -04:00
7d7500ba05 Merge pull request #5492 from sbwalker/dev
remove content from readme,md
2025-08-15 10:44:07 -04:00
247fc5248b remove content from readme,md 2025-08-15 10:43:52 -04:00
85fcd1ed33 Merge pull request #5490 from sbwalker/dev
improve error handling for the scenario where a connection string does not exist in appsettings.json for a tenant
2025-08-14 15:58:13 -04:00
4ab8f8cc25 improve error handling for the scenario where a connection string does not exist in appsettings.json for a tenant 2025-08-14 15:57:50 -04:00
ccdfe9bc26 Update appsettings.release.json 2025-08-14 15:38:38 -04:00
dc47961cc2 Update appsettings.json 2025-08-14 15:36:36 -04:00
87394cd330 Merge pull request #5489 from sbwalker/dev
migrate database providers to core framework
2025-08-14 15:20:11 -04:00
b5a9c32c3e migrate database providers to core framework 2025-08-14 15:19:52 -04:00
d16521f037 Merge pull request #5487 from sbwalker/dev
fix #5462 add logic to check if database already exists before calling EnsureCreated
2025-08-13 16:13:59 -04:00
b553b16049 fix #5462 add logic to check if database already exists before calling EnsureCreated 2025-08-13 16:13:38 -04:00
784548be57 Merge pull request #5486 from sbwalker/dev
Include support for DateTime values in RewriteValue method
2025-08-13 15:13:13 -04:00
cf96a80ead Include support for DateTime values in RewriteValue method 2025-08-13 15:12:58 -04:00
ede6babeaf Merge pull request #5485 from sbwalker/dev
fix compatibility issue
2025-08-13 14:55:32 -04:00
9a57cae4bd fix compatibility issue 2025-08-13 14:55:18 -04:00
1a296bf58c Merge pull request #5484 from sbwalker/dev
consolidate Infrastructure interface and implementation classes
2025-08-13 14:45:01 -04:00
e900d2f35a consolidate Infrastructure interface and implementation classes 2025-08-13 14:44:42 -04:00
69d2d3d942 Merge pull request #5483 from sbwalker/dev
add authorization convenience methods to ModuleBase
2025-08-13 08:18:33 -04:00
b7ff49bdb2 add authorization convenience methods to ModuleBase 2025-08-13 08:18:16 -04:00
3284e0f60a Merge pull request #5482 from sbwalker/dev
upgrade SQLitePCLRaw.bundle_e_sqlite3 package and add logic to release.cmd to remove android and ios client runtimes
2025-08-12 16:37:03 -04:00
8cec847188 upgrade SQLitePCLRaw.bundle_e_sqlite3 package and add logic to release.cmd to remove android and ios client runtimes 2025-08-12 16:36:39 -04:00
2d44644a3d Merge pull request #5481 from sbwalker/dev
bump version to 6.1.5
2025-08-12 16:08:46 -04:00
e32f55e433 bump version to 6.1.5 2025-08-12 16:08:30 -04:00
362c4ae272 Merge pull request #5480 from sbwalker/dev
ensure all install config settings are populated
2025-08-12 15:33:33 -04:00
eb8ad04557 ensure all install config settings are populated 2025-08-12 15:33:17 -04:00
d1455596c6 Merge pull request #5479 from sbwalker/dev
add InsertData(), UpdateData(), DeleteData() migration methods and improve RewriteValue() abstraction
2025-08-12 15:00:12 -04:00
6142bfc5db add InsertData(), UpdateData(), DeleteData() migration methods and improve RewriteValue() abstraction 2025-08-12 14:59:51 -04:00
dbda0be53b Merge pull request #5478 from sbwalker/dev
follow same pattern as core framework
2025-08-11 17:11:15 -04:00
bf932719b2 follow same pattern as core framework 2025-08-11 17:10:58 -04:00
60e6e33805 Merge pull request #5477 from sbwalker/dev
consolidate Service interface and implementation classes
2025-08-11 16:53:50 -04:00
64b8b5d3c8 consolidate Service interface and implementation classes 2025-08-11 16:53:32 -04:00
8bce40c2b8 Merge pull request #5476 from sbwalker/dev
consolidate interface and implementation classes
2025-08-11 16:36:29 -04:00
b3f6194fda consolidate interface and implementation classes 2025-08-11 16:36:09 -04:00
fdbf2ab0a7 Merge pull request #5475 from sbwalker/dev
fix issue with Admin Site Template
2025-08-11 16:22:40 -04:00
d7eb0dc509 fix issue with Admin Site Template 2025-08-11 16:22:22 -04:00
1a34bf4460 Merge pull request #5469 from sbwalker/dev
add missing delete setting API method
2025-08-07 15:07:46 -04:00
4cf1b5c0e7 add missing delete setting API method 2025-08-07 15:07:33 -04:00
764b883579 Merge pull request #5468 from sbwalker/dev
only hosts should be allowed to view/edit SMTP settings
2025-08-07 14:42:40 -04:00
3bd6767138 only hosts should be allowed to view/edit SMTP settings 2025-08-07 14:42:24 -04:00
bef9025b6c Merge pull request #5467 from sbwalker/dev
fix malformed bold tag
2025-08-07 14:32:09 -04:00
a37f07d20b fix malformed bold tag 2025-08-07 14:31:57 -04:00
638946b1f5 Merge pull request #5466 from sbwalker/dev
performance improvement to filter settings in database
2025-08-07 14:30:27 -04:00
30c869ff2a performance improvement to filter settings in database 2025-08-07 14:30:13 -04:00
2c3fda9cb5 Merge pull request #5464 from sbwalker/dev
fix #5461 - handle MinDate and MaxDate
2025-08-07 10:58:54 -04:00
b11a7a678c fix #5461 - handle MinDate and MaxDate 2025-08-07 10:58:33 -04:00
02011f9ce5 Merge pull request #5463 from leigh-pointer/REFsUpdate908
Updated Project Refs 9.0.8
2025-08-07 10:38:02 -04:00
39ae6a76cd Updated Project Refs 9.0.8
# SQLitePCLRaw.bundle_e_sqlite3 was not updated.
Oqtane Framework
Project Templates Module and Theme
MAUI solution
2025-08-07 08:09:34 +02:00
31684bf7ca Merge pull request #5458 from sbwalker/dev
rolling back to SQLitePCLRaw.bundle_e_sqlite3 version 2.1.11
2025-08-04 17:18:42 -04:00
7b36f8d122 rolling back to SQLitePCLRaw.bundle_e_sqlite3 version 2.1.11 2025-08-04 17:18:25 -04:00
f2a0be4f57 Merge pull request #5457 from sbwalker/dev
resolve interactive page load
2025-08-04 13:09:52 -04:00
2cefab1c64 resolve interactive page load 2025-08-04 13:09:37 -04:00
5b4b96f065 Merge pull request #5456 from sbwalker/dev
improve FileManager performance
2025-08-04 13:06:34 -04:00
77949331e2 improve FileManager performance 2025-08-04 13:06:16 -04:00
4f8c4f47e2 Merge pull request #5454 from sbwalker/dev
improve FileManager performance when ShowFiles is disabled
2025-08-02 09:46:20 -04:00
334137454e improve FileManager performance when ShowFiles is disabled 2025-08-02 09:46:02 -04:00
af7ea3efa8 Merge pull request #5453 from sbwalker/dev
improve interactive rendering logic
2025-08-01 15:43:36 -04:00
6119417331 improve interactive rendering logic 2025-08-01 15:43:21 -04:00
580397a82d Merge pull request #5452 from sbwalker/dev
add active/deleted filter in User Management
2025-08-01 14:45:54 -04:00
23c3c47db4 add active/deleted filter in User Management 2025-08-01 14:45:40 -04:00
df3073fb12 Merge pull request #5451 from sbwalker/dev
improve broken link handling
2025-08-01 10:54:55 -04:00
aa9664e187 improve broken link handling 2025-08-01 10:54:40 -04:00
44f4aee55d Merge pull request #5450 from sbwalker/dev
fix AddModuleMessage not displaying messages in Interactive render mode
2025-08-01 09:15:12 -04:00
02861b8e01 fix AddModuleMessage not displaying messages in Interactive render mode 2025-08-01 09:14:58 -04:00
9607110381 Merge pull request #5449 from sbwalker/dev
Resolve issue where visitor cookie was not being added to HttpClient. This was because cookie values cannot contain spaces and therefore need to be Url encoded.
2025-08-01 07:52:25 -04:00
9ae12ff678 Resolve issue where visitor cookie was not being added to HttpClient. This was because cookie values cannot contain spaces and therefore need to be Url encoded. 2025-08-01 07:51:58 -04:00
2bcb8636ca Merge pull request #5448 from sbwalker/dev
log the logout event
2025-07-31 16:23:56 -04:00
4c2960eeae log the logout event 2025-07-31 16:23:40 -04:00
7e2c76e872 Merge pull request #5447 from sbwalker/dev
improve notification message when email is verified by administrator
2025-07-31 16:06:57 -04:00
30fcde7157 improve notification message when email is verified by administrator 2025-07-31 16:06:42 -04:00
4971d3317d Merge pull request #5446 from thabaum/6.1.5-oqtane.server-update-dependencies
Fixes #5445: Updates Oqtane.Server.csproj Package Dependencies
2025-07-31 15:54:55 -04:00
85ae7b01b8 Update Oqtane.Server.csproj Package Dependencies 2025-07-31 09:11:09 -07:00
9f566624fe Merge pull request #5444 from sbwalker/dev
resolve interactive rendering issue
2025-07-31 11:04:37 -04:00
50fa95dff9 resolve interactive rendering issue 2025-07-31 11:04:22 -04:00
752083e9eb Update README.md 2025-07-30 15:29:19 -04:00
582c7f83f7 Merge pull request #5440 from sbwalker/dev
update Azure ARM template to 6.1.4
2025-07-30 15:23:29 -04:00
d95104cb92 update Azure ARM template to 6.1.4 2025-07-30 15:23:16 -04:00
6c58ab4554 Merge pull request #5439 from oqtane/master
6.1.4 Release
2025-07-30 15:11:01 -04:00
085187cfac 6.1.4 Release
6.1.4 Release
2025-07-30 15:10:42 -04:00
3d0f0a5adc Merge pull request #5437 from sbwalker/dev
synchronize app.css with .NET MAUI
2025-07-30 13:40:41 -04:00
eae8b431ee synchronize app.css with .NET MAUI 2025-07-30 13:40:25 -04:00
e3a34446c0 Merge pull request #5436 from sbwalker/dev
synchronize interop,js with .NET MAUI
2025-07-30 13:35:56 -04:00
bfe57c3ac7 synchronize interop,js with .NET MAUI 2025-07-30 13:35:39 -04:00
d4001be716 Merge pull request #5435 from sbwalker/dev
fix #5364 - add ability to specify preferred Container per Pane
2025-07-30 10:43:51 -04:00
662a1817f2 fix #5364 - add ability to specify preferred Container per Pane 2025-07-30 10:43:36 -04:00
2c99ef412d Merge pull request #5434 from sbwalker/dev
use consistent terminology
2025-07-30 10:01:11 -04:00
f53ed5b13b use consistent terminology 2025-07-30 10:00:57 -04:00
b5d51838c6 Merge pull request #5433 from sbwalker/dev
allow specific time zones to be excluded
2025-07-30 09:29:56 -04:00
92fd70198a allow specific time zones to be excluded 2025-07-30 09:29:43 -04:00
7f1990f851 Merge pull request #5432 from sbwalker/dev
fix incorrect resource reference
2025-07-30 08:48:04 -04:00
797d7afc3e fix incorrect resource reference 2025-07-30 08:47:50 -04:00
c5a23cdfa0 Merge pull request #5431 from sbwalker/dev
update Oqtane theme to Bootstrap 5.3.7
2025-07-30 08:30:54 -04:00
906358f1f8 update Oqtane theme to Bootstrap 5.3.7 2025-07-30 08:30:40 -04:00
638f2a59c5 Merge pull request #5430 from sbwalker/dev
use margin rather than padding
2025-07-30 08:16:20 -04:00
cf9b4b869c use margin rather than padding 2025-07-30 08:16:07 -04:00
671c52fbbb Merge pull request #5429 from leigh-pointer/CDN-Bootstrap
Discussion #5426 updated and returned to https://cdnjs.com/
2025-07-30 08:07:52 -04:00
6c0e2a62e7 Discussion #5426 updated and returned to https://cdnjs.com/
Updated and styles tested - reload.js needs still testing?
2025-07-30 12:53:59 +02:00
1b78c9ad81 Merge pull request #5428 from sbwalker/dev
use consistent naming
2025-07-29 16:36:43 -04:00
7a4b98aec9 use consistent naming 2025-07-29 16:36:28 -04:00
9ef6c15014 Merge pull request #5427 from sbwalker/dev
fix #5349 - send verification email if unverified user attempts to login, add ability to enable/disable email verification per site
2025-07-29 16:20:37 -04:00
f4cea3fe03 fix #5349 - send verification email if unverified user attempts to login, add ability to enable/disable email verification per site 2025-07-29 16:20:07 -04:00
5dd9b1ec91 Merge pull request #5425 from sbwalker/dev
fix #5346 - deleting role should remove associated useroles
2025-07-29 09:05:54 -04:00
658059806b fix #5346 - deleting role should remove associated useroles 2025-07-29 09:05:37 -04:00
4f8a18451c Merge pull request #5424 from sbwalker/dev
fix #5346 - deleting role should remove associated permissions
2025-07-29 08:40:54 -04:00
b1770ebb76 fix #5346 - deleting role should remove associated permissions 2025-07-29 08:40:38 -04:00
6923065d86 Merge pull request #5423 from sbwalker/dev
fix #5348 - ensure time zones work consistently on all platforms
2025-07-29 08:11:56 -04:00
9f097521f6 fix #5348 - ensure time zones work consistently on all platforms 2025-07-29 08:11:42 -04:00
235e5c1d3a Merge pull request #5421 from sbwalker/dev
improve TimeZoneService
2025-07-28 17:00:47 -04:00
e179976fe8 improve TimeZoneService 2025-07-28 17:00:27 -04:00
082726b405 Merge pull request #5420 from sbwalker/dev
fix #5372 - add support for sending SMTP emails using OAuth
2025-07-28 10:26:34 -04:00
91c5309855 fix #5372 - add support for sending SMTP emails using OAuth 2025-07-28 10:26:18 -04:00
92be1e7a5c Merge pull request #5419 from sbwalker/dev
add OAuth support to Notification Job (#5372)
2025-07-28 09:06:55 -04:00
cceda1db1e add OAuth support to Notification Job (#5372) 2025-07-28 09:06:36 -04:00
a59191cea7 Merge pull request #5416 from sbwalker/dev
fix #5414 - add DelimitName database provider method to better support MigrationBuilder.Sql() operations
2025-07-25 15:22:54 -04:00
b0dee4a60c fix #5414 - add DelimitName database provider method to better support MigrationBuilder.Sql() operations 2025-07-25 15:22:26 -04:00
3f33f2b9df Merge pull request #5412 from sbwalker/dev
fix #5410 - allow duplicate email addresses
2025-07-23 16:40:27 -04:00
97116b4e0c fix #5410 - allow duplicate email addresses 2025-07-23 16:40:12 -04:00
a5f51ff9a1 Merge pull request #5411 from sbwalker/dev
localize time zone names
2025-07-23 14:52:34 -04:00
962488fd34 localize time zone names 2025-07-23 14:52:18 -04:00
190d973b77 Merge pull request #5406 from leigh-pointer/Refs
Solutions References update
2025-07-22 16:14:13 -04:00
397e0b3f71 Merge pull request #5408 from sbwalker/dev
improve user experience of permissions grid
2025-07-22 16:12:48 -04:00
83ba9ca73e improve user experience of permissions grid 2025-07-22 16:07:52 -04:00
3d08138686 Merge pull request #5407 from sbwalker/dev
improve documentation
2025-07-22 09:23:42 -04:00
262fa6b99b improve documentation 2025-07-22 09:23:26 -04:00
372db9dcfa Solutions References update
MySql.Data 9.4.0
HtmlAgilityPack 1.12.2
2025-07-22 07:45:03 +02:00
e9dc52919c Merge pull request #5405 from sbwalker/dev
fix Control Panel to initialize extended module permissions when module is added or copied
2025-07-21 16:34:56 -04:00
a981dd0e97 fix Control Panel to initialize extended module permissions when module is added or copied 2025-07-21 16:34:34 -04:00
7c2775119b Merge pull request #5404 from sbwalker/dev
add new option to FileManager component to anonymize filenames during upload
2025-07-21 09:14:30 -04:00
0be7f1bdb5 add new option to FileManager component to anonymize filenames during upload 2025-07-21 09:14:07 -04:00
8446b9e8d5 Merge pull request #5392 from thabaum/patch-15 2025-07-15 14:28:50 -04:00
ce404668d3 Merge pull request #5391 from thabaum/6.1.4-dependencies 2025-07-15 14:28:28 -04:00
948fab50ee [FIX] #5164 – Raise z‑index for .app‑moduleactions .dropdown‑menu to 9999 2025-07-14 17:10:07 -07:00
9690f1df48 [FIX] oqtane#5164 – Raise z‑index for .app‑moduleactions .dropdown‑menu to 9999 2025-07-14 17:09:24 -07:00
5a24f87293 [FIX] #5164 ‑ Set z‑index for .dropdown‑menu in .app‑moduleactions 2025-07-14 17:07:39 -07:00
d2ff49fe73 [FIX] #5164 - Set z-index for .dropdown-menu in .app-moduleactions 2025-07-14 16:10:50 -07:00
e9035df9d2 Update Package Dependencies 2025-07-14 13:40:52 -07:00
1ddf21f4fc Merge pull request #5387 from mdmontesinos/feat-nodatime 2025-07-11 07:18:10 -04:00
63d2ded038 Merge branch 'dev' into feat-nodatime 2025-07-11 09:07:12 +02:00
7b8e0e48c0 Merge pull request #5385 from leigh-pointer/907 2025-07-11 02:28:38 -04:00
bb52402a17 feat: handle timezones and conversions with NodaTime 2025-07-09 12:09:00 +02:00
13d9cb461b Update Oqtane Maui project to 9.0.7 2025-07-09 03:42:26 +02:00
0a994afd67 Update References .NetCore 9.0.7 2025-07-09 02:52:30 +02:00
57a1257750 Merge pull request #5384 from sbwalker/dev
update External Login default values for Facebook OAuth2
2025-07-08 16:27:58 -04:00
b0c1d36bab update External Login default values for Facebook OAuth2 2025-07-08 16:27:35 -04:00
0621751968 Merge pull request #5383 from sbwalker/dev
resolve issue where IDP fails to provide email claim resulting in External Login Remote Failure due to dbo.AspNetUsers requiring a unique email value for each user
2025-07-08 16:04:37 -04:00
461330773a resolve issue where IDP fails to provide email claim resulting in External Login Remote Failure due to dbo.AspNetUsers requiring a unique email value for each user 2025-07-08 16:04:19 -04:00
818a97cc2c Merge pull request #5382 from sbwalker/dev
bump version to 6.1.4
2025-07-08 13:20:43 -04:00
17045073c8 bump version to 6.1.4 2025-07-08 13:20:28 -04:00
7a818ee698 Merge pull request #5381 from sbwalker/dev
update to .NET SDK 9.0.6
2025-07-08 13:15:10 -04:00
668e0cb4eb update to .NET SDK 9.0.6 2025-07-08 13:14:53 -04:00
741b16ca4e Merge pull request #5380 from sbwalker/dev
update to .NET SDK 9.0.6
2025-07-08 13:12:06 -04:00
85a376b17d update to .NET SDK 9.0.6 2025-07-08 13:11:52 -04:00
df86cd909c Merge pull request #5379 from sbwalker/dev
update to .NET SDK 9.0.6
2025-07-08 13:09:24 -04:00
ac236607f5 update to .NET SDK 9.0.6 2025-07-08 13:09:10 -04:00
19813b7eb6 Merge pull request #5378 from sbwalker/dev
remove unused variable
2025-07-07 12:42:51 -04:00
cb5e4e076f remove unused variable 2025-07-07 12:42:35 -04:00
48fca77f59 Merge pull request #5276 from leigh-pointer/Bootstrap
Updated to Bootstrap 5.3.5
2025-07-07 12:40:39 -04:00
76372451aa Merge pull request #5370 from zyhfish/task/fix-5363
Fix #5363: update SettingService.MergeSettings.
2025-07-07 12:40:14 -04:00
34cd197122 Merge pull request #5376 from mdmontesinos/feat-mailkit
feat: replace System.Net.Mail with MailKit (#5372)
2025-07-07 12:40:05 -04:00
6b567364f9 feat: use appropriate UseSSL equivalent in MailKit 2025-07-04 14:55:02 +02:00
711de49571 feat: replace System.Net.Mail with MailKit (#5372) 2025-07-04 12:55:40 +02:00
9a0f7ad83f Merge pull request #5375 from sbwalker/dev
fix #5374 Visitor Settings not returned due to change in Visitor cookie format
2025-07-03 16:45:19 -04:00
0d3d693799 fix #5374 Visitor Settings not returned due to change in Visitor cookie format 2025-07-03 16:44:59 -04:00
Ben
b63590d6c7 Fix #5363: update SettingService.MergeSettings. 2025-07-03 15:42:11 +08:00
5f3a3d4d54 Merge remote-tracking branch 'upstream/dev' into Bootstrap 2025-06-13 19:58:18 +02:00
b1a8c28283 Merge pull request #5356 from leigh-pointer/Schedular
Fix for Scheduled Jobs UI #5354
2025-06-13 08:36:47 -04:00
1412737036 Date / Time validations
This PR ensures time fields are required when dates are set, using Oqtane validation and dynamically toggles the required attribute on time inputs when their corresponding date fields have values. Benefits:
- Uses Oqtane's validation for a polished UX.
- Reduces custom validation code.
- Aligns with our internal form logic.

- Tested across all date/time scenarios—works flawlessly!
**Testing Confirmed:**
- Date + Time Provided → Saves successfully.
- No Date + No Time → Optional (no validation).
- Date + No Time → Browser blocks submission with icon error.
2025-06-10 12:27:55 +02:00
ffb3f4fa50 Merge pull request #5353 from mdmontesinos/fix-cookieconsent
fix #5352: remove requests to cookie consent service when not enabled
2025-06-09 15:57:59 -04:00
ff450ca43a Fix for Scheduled Jobs UI #5354
This PR addresses an issue where null date/time values could cause exceptions when processing job scheduling.
Changes Made:
- Added proper null checks for _startDate, _startTime, _endDate, _endTime, _nextDate, and _nextTime
- Improved parsing safety for _retentionHistory using int.TryParse()
- Added validation to fail early with meaningful error messages

Impact:

Prevents NullReferenceException and InvalidOperationException when date/time fields are missing
2025-06-09 10:29:43 +02:00
d4f0805108 fix #5352: remove requests to cookie consent service when not enabled 2025-06-06 10:05:40 +02:00
64ce69d1c7 Merge pull request #5351 from sbwalker/dev
stop gap fix to mitigate date conversion exceptions on WebAssembly
2025-06-05 10:38:51 -04:00
ca3cb48091 Merge branch 'dev' of https://github.com/sbwalker/oqtane.framework into dev 2025-06-05 10:37:31 -04:00
85085bf4c7 stop gap fix to mitigate date conversion exceptions on WebAssembly 2025-06-05 10:37:25 -04:00
873af6b598 Merge pull request #5349 from leigh-pointer/References
Server References Updated
2025-06-05 09:32:34 -04:00
c423895f31 Merge pull request #5350 from sbwalker/dev
rendering optimizations
2025-06-05 09:32:12 -04:00
4418e27c29 rendering optimizations 2025-06-05 09:31:54 -04:00
f776977af8 Server References Updated
update SixLabors.ImageSharp
update Swashbuckle.AspNetCore
2025-06-04 13:28:49 +02:00
c13ce3d0f1 Update Index.razor
Deprecated .text-muted will be replaced by .text-body-secondary in v6.
2025-06-03 15:24:43 +02:00
2c4c669ea2 Merge remote-tracking branch 'upstream/dev' into Bootstrap 2025-05-30 16:06:19 +02:00
018737c42a Merge remote-tracking branch 'upstream/dev' into Bootstrap 2025-05-15 11:52:51 +02:00
3811b8f0c0 Theme Template updated 2025-05-07 11:46:07 +02:00
d81514e9be Update for Blazor Theme 2025-05-02 12:19:58 +02:00
14b0d7abf0 Updated to Bootstrap 5
Updated to Bootstrap 5.3.5
Update bootswatch Cyborg to 5.3.5 using https://cdn.jsdelivr.net because it is not available at https://cdnjs.com/libraries
2025-05-02 12:16:55 +02:00
1556 changed files with 8737 additions and 129328 deletions

View File

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

View File

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

9
Oqtane.Application/.gitignore vendored Normal file
View File

@ -0,0 +1,9 @@
.vs/
bin/
obj/
*.user
artifacts/
msbuild.binlog
.vscode/
*.binlog
*.nupkg

View File

@ -0,0 +1,19 @@
{
"$schema": "http://json.schemastore.org/template",
"author": "Shaun Walker",
"classifications": [
"Web",
"ASP.NET",
"Blazor",
"Oqtane"
],
"tags": {
"language": "C#",
"type": "project"
},
"identity": "Oqtane.Application.Template",
"name": "Oqtane Application Solution For Blazor",
"shortName": "oqtane-app",
"sourceName": "Oqtane.Application",
"preferNameDirectory": true
}

View File

@ -0,0 +1,3 @@
using Microsoft.Extensions.Localization;
[assembly: RootNamespace("Oqtane.Application.Client")]

View File

@ -1,7 +1,7 @@
using Microsoft.JSInterop;
using System.Threading.Tasks;
namespace [Owner].Module.[Module]
namespace Oqtane.Application
{
public class Interop
{

View File

@ -1,10 +1,10 @@
@using Oqtane.Modules.Controls
@using [Owner].Module.[Module].Services
@using [Owner].Module.[Module].Models
@using Oqtane.Application.Services
@using Oqtane.Application.Models
@namespace [Owner].Module.[Module]
@namespace Oqtane.Application.MyModule
@inherits ModuleBase
@inject I[Module]Service [Module]Service
@inject IMyModuleService MyModuleService
@inject NavigationManager NavigationManager
@inject IStringLocalizer<Edit> Localizer
@ -31,11 +31,11 @@
public override string Actions => "Add,Edit";
public override string Title => "Manage [Module]";
public override string Title => "Manage MyModule";
public override List<Resource> Resources => new List<Resource>()
{
new Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css" }
new Stylesheet(ModulePath() + "Module.css")
};
private ElementReference form;
@ -55,20 +55,20 @@
if (PageState.Action == "Edit")
{
_id = Int32.Parse(PageState.QueryString["id"]);
[Module] [Module] = await [Module]Service.Get[Module]Async(_id, ModuleState.ModuleId);
if ([Module] != null)
MyModule MyModule = await MyModuleService.GetMyModuleAsync(_id, ModuleState.ModuleId);
if (MyModule != null)
{
_name = [Module].Name;
_createdby = [Module].CreatedBy;
_createdon = [Module].CreatedOn;
_modifiedby = [Module].ModifiedBy;
_modifiedon = [Module].ModifiedOn;
_name = MyModule.Name;
_createdby = MyModule.CreatedBy;
_createdon = MyModule.CreatedOn;
_modifiedby = MyModule.ModifiedBy;
_modifiedon = MyModule.ModifiedOn;
}
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading [Module] {[Module]Id} {Error}", _id, ex.Message);
await logger.LogError(ex, "Error Loading MyModule {MyModuleId} {Error}", _id, ex.Message);
AddModuleMessage(Localizer["Message.LoadError"], MessageType.Error);
}
}
@ -83,18 +83,18 @@
{
if (PageState.Action == "Add")
{
[Module] [Module] = new [Module]();
[Module].ModuleId = ModuleState.ModuleId;
[Module].Name = _name;
[Module] = await [Module]Service.Add[Module]Async([Module]);
await logger.LogInformation("[Module] Added {[Module]}", [Module]);
MyModule MyModule = new MyModule();
MyModule.ModuleId = ModuleState.ModuleId;
MyModule.Name = _name;
MyModule = await MyModuleService.AddMyModuleAsync(MyModule);
await logger.LogInformation("MyModule Added {MyModule}", MyModule);
}
else
{
[Module] [Module] = await [Module]Service.Get[Module]Async(_id, ModuleState.ModuleId);
[Module].Name = _name;
await [Module]Service.Update[Module]Async([Module]);
await logger.LogInformation("[Module] Updated {[Module]}", [Module]);
MyModule MyModule = await MyModuleService.GetMyModuleAsync(_id, ModuleState.ModuleId);
MyModule.Name = _name;
await MyModuleService.UpdateMyModuleAsync(MyModule);
await logger.LogInformation("MyModule Updated {MyModule}", MyModule);
}
NavigationManager.NavigateTo(NavigateUrl());
}
@ -105,7 +105,7 @@
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Saving [Module] {Error}", ex.Message);
await logger.LogError(ex, "Error Saving MyModule {Error}", ex.Message);
AddModuleMessage(Localizer["Message.SaveError"], MessageType.Error);
}
}

View File

@ -1,32 +1,32 @@
@using [Owner].Module.[Module].Services
@using [Owner].Module.[Module].Models
@using Oqtane.Application.Services
@using Oqtane.Application.Models
@namespace [Owner].Module.[Module]
@namespace Oqtane.Application.MyModule
@inherits ModuleBase
@inject I[Module]Service [Module]Service
@inject IMyModuleService MyModuleService
@inject NavigationManager NavigationManager
@inject IStringLocalizer<Index> Localizer
@if (_[Module]s == null)
@if (_MyModules == null)
{
<p><em>Loading...</em></p>
}
else
{
<ActionLink Action="Add" Security="SecurityAccessLevel.Edit" Text="Add [Module]" ResourceKey="Add" />
<ActionLink Action="Add" Security="SecurityAccessLevel.Edit" Text="Add MyModule" ResourceKey="Add" />
<br />
<br />
@if (@_[Module]s.Count != 0)
@if (@_MyModules.Count != 0)
{
<Pager Items="@_[Module]s">
<Pager Items="@_MyModules">
<Header>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>@Localizer["Name"]</th>
</Header>
<Row>
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.[Module]Id.ToString())" ResourceKey="Edit" /></td>
<td><ActionDialog Header="Delete [Module]" Message="Are You Sure You Wish To Delete This [Module]?" Action="Delete" Security="SecurityAccessLevel.Edit" Class="btn btn-danger" OnClick="@(async () => await Delete(context))" ResourceKey="Delete" Id="@context.[Module]Id.ToString()" /></td>
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.MyModuleId.ToString())" ResourceKey="Edit" /></td>
<td><ActionDialog Header="Delete MyModule" Message="Are You Sure You Wish To Delete This MyModule?" Action="Delete" Security="SecurityAccessLevel.Edit" Class="btn btn-danger" OnClick="@(async () => await Delete(context))" ResourceKey="Delete" Id="@context.MyModuleId.ToString()" /></td>
<td>@context.Name</td>
</Row>
</Pager>
@ -38,41 +38,39 @@ else
}
@code {
public override string RenderMode => RenderModes.Static;
public override List<Resource> Resources => new List<Resource>()
{
new Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css" },
new Resource { ResourceType = ResourceType.Script, Url = ModulePath() + "Module.js" }
new Stylesheet(ModulePath() + "Module.css"),
new Script(ModulePath() + "Module.js")
};
List<[Module]> _[Module]s;
List<Models.MyModule> _MyModules;
protected override async Task OnInitializedAsync()
{
try
{
_[Module]s = await [Module]Service.Get[Module]sAsync(ModuleState.ModuleId);
_MyModules = await MyModuleService.GetMyModulesAsync(ModuleState.ModuleId);
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading [Module] {Error}", ex.Message);
await logger.LogError(ex, "Error Loading MyModule {Error}", ex.Message);
AddModuleMessage(Localizer["Message.LoadError"], MessageType.Error);
}
}
private async Task Delete([Module] [Module])
private async Task Delete(MyModule MyModule)
{
try
{
await [Module]Service.Delete[Module]Async([Module].[Module]Id, ModuleState.ModuleId);
await logger.LogInformation("[Module] Deleted {[Module]}", [Module]);
_[Module]s = await [Module]Service.Get[Module]sAsync(ModuleState.ModuleId);
await MyModuleService.DeleteMyModuleAsync(MyModule.MyModuleId, ModuleState.ModuleId);
await logger.LogInformation("MyModule Deleted {MyModule}", MyModule);
_MyModules = await MyModuleService.GetMyModulesAsync(ModuleState.ModuleId);
StateHasChanged();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Deleting [Module] {[Module]} {Error}", [Module], ex.Message);
await logger.LogError(ex, "Error Deleting MyModule {MyModule} {Error}", MyModule, ex.Message);
AddModuleMessage(Localizer["Message.DeleteError"], MessageType.Error);
}
}

View File

@ -0,0 +1,19 @@
using Oqtane.Models;
using Oqtane.Modules;
namespace Oqtane.Application.MyModule
{
public class ModuleInfo : IModule
{
public ModuleDefinition ModuleDefinition => new ModuleDefinition
{
Name = "MyModule",
Description = "Example module",
Version = "1.0.0",
ServerManagerType = "Oqtane.Application.Manager.MyModuleManager, Oqtane.Application.Server.Oqtane",
ReleaseVersions = "1.0.0",
Dependencies = "Oqtane.Application.Shared.Oqtane",
PackageName = "Oqtane.Application"
};
}
}

View File

@ -1,4 +1,4 @@
@namespace [Owner].Module.[Module]
@namespace Oqtane.Application.MyModule
@inherits ModuleBase
@inject ISettingService SettingService
@inject IStringLocalizer<Settings> Localizer
@ -13,8 +13,8 @@
</div>
@code {
private string resourceType = "[Owner].Module.[Module].Settings, [Owner].Module.[Module].Client.Oqtane"; // for localization
public override string Title => "[Module] Settings";
private string resourceType = "Oqtane.Application.Settings, Oqtane.Application.Client.Oqtane"; // for localization
public override string Title => "MyModdule Settings";
string _value;

View File

@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Version>1.0.0</Version>
<AssemblyName>Oqtane.Application.Client.Oqtane</AssemblyName>
<NoDefaultLaunchSettingsFile>true</NoDefaultLaunchSettingsFile>
<StaticWebAssetProjectMode>Default</StaticWebAssetProjectMode>
<BlazorWebAssemblyEnableLinking>false</BlazorWebAssemblyEnableLinking>
<BlazorWebAssemblyLoadAllGlobalizationData>true</BlazorWebAssemblyLoadAllGlobalizationData>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Shared\Oqtane.Application.Shared.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Oqtane.Client" Version="6.2.0" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,13 @@
using System.Threading.Tasks;
namespace Oqtane.Application.Client
{
internal class Program
{
static async Task Main(string[] args)
{
// defer client startup to Oqtane - do not modify
await Oqtane.Client.Program.Main(args);
}
}
}

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="Name.Text" xml:space="preserve">
<value>Name: </value>
</data>
<data name="Name.HelpText" xml:space="preserve">
<value>Enter the name</value>
</data>
<data name="Save" xml:space="preserve">
<value>Save</value>
</data>
<data name="Cancel" xml:space="preserve">
<value>Cancel</value>
</data>
<data name="Message.SaveValidation" xml:space="preserve">
<value>Please Provide All Required Information</value>
</data>
<data name="Message.SaveError" xml:space="preserve">
<value>Error Saving MyModule</value>
</data>
<data name="Message.LoadError" xml:space="preserve">
<value>Error Loading MyModule</value>
</data>
</root>

View File

@ -0,0 +1,147 @@
<?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="Name" xml:space="preserve">
<value>Name</value>
</data>
<data name="Add.Text" xml:space="preserve">
<value>Add MyModule</value>
</data>
<data name="Edit.Text" xml:space="preserve">
<value>Edit</value>
</data>
<data name="Delete.Text" xml:space="preserve">
<value>Delete</value>
</data>
<data name="Delete.Header" xml:space="preserve">
<value>Delete MyModule</value>
</data>
<data name="Delete.Message" xml:space="preserve">
<value>Are You Sure You Wish To Delete This MyModule?</value>
</data>
<data name="Message.DisplayNone" xml:space="preserve">
<value>No MyModules To Display</value>
</data>
<data name="Message.LoadError" xml:space="preserve">
<value>Error Loading MyModule</value>
</data>
<data name="Message.DeleteError" xml:space="preserve">
<value>Error Deleting MyModule</value>
</data>
</root>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema

View File

@ -0,0 +1,55 @@
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Oqtane.Services;
using Oqtane.Shared;
namespace Oqtane.Application.Services
{
public interface IMyModuleService
{
Task<List<Models.MyModule>> GetMyModulesAsync(int ModuleId);
Task<Models.MyModule> GetMyModuleAsync(int MyModuleId, int ModuleId);
Task<Models.MyModule> AddMyModuleAsync(Models.MyModule MyModule);
Task<Models.MyModule> UpdateMyModuleAsync(Models.MyModule MyModule);
Task DeleteMyModuleAsync(int MyModuleId, int ModuleId);
}
public class MyModuleService : ServiceBase, IMyModuleService
{
public MyModuleService(HttpClient http, SiteState siteState) : base(http, siteState) { }
private string Apiurl => CreateApiUrl("MyModule");
public async Task<List<Models.MyModule>> GetMyModulesAsync(int ModuleId)
{
List<Models.MyModule> Tasks = await GetJsonAsync<List<Models.MyModule>>(CreateAuthorizationPolicyUrl($"{Apiurl}?moduleid={ModuleId}", EntityNames.Module, ModuleId), Enumerable.Empty<Models.MyModule>().ToList());
return Tasks.OrderBy(item => item.Name).ToList();
}
public async Task<Models.MyModule> GetMyModuleAsync(int MyModuleId, int ModuleId)
{
return await GetJsonAsync<Models.MyModule>(CreateAuthorizationPolicyUrl($"{Apiurl}/{MyModuleId}/{ModuleId}", EntityNames.Module, ModuleId));
}
public async Task<Models.MyModule> AddMyModuleAsync(Models.MyModule MyModule)
{
return await PostJsonAsync<Models.MyModule>(CreateAuthorizationPolicyUrl($"{Apiurl}", EntityNames.Module, MyModule.ModuleId), MyModule);
}
public async Task<Models.MyModule> UpdateMyModuleAsync(Models.MyModule MyModule)
{
return await PutJsonAsync<Models.MyModule>(CreateAuthorizationPolicyUrl($"{Apiurl}/{MyModule.MyModuleId}", EntityNames.Module, MyModule.ModuleId), MyModule);
}
public async Task DeleteMyModuleAsync(int MyModuleId, int ModuleId)
{
await DeleteAsync(CreateAuthorizationPolicyUrl($"{Apiurl}/{MyModuleId}/{ModuleId}", EntityNames.Module, ModuleId));
}
}
}

View File

@ -0,0 +1,18 @@
using Microsoft.Extensions.DependencyInjection;
using System.Linq;
using Oqtane.Services;
using Oqtane.Application.Services;
namespace Oqtane.Application.Startup
{
public class ClientStartup : IClientStartup
{
public void ConfigureServices(IServiceCollection services)
{
if (!services.Any(s => s.ServiceType == typeof(IMyModuleService)))
{
services.AddScoped<IMyModuleService, MyModuleService>();
}
}
}
}

View File

@ -1,4 +1,4 @@
@namespace [Owner].Theme.[Theme]
@namespace Oqtane.Application.MyTheme
@inherits ContainerBase
@inject ISettingService SettingService
@ -24,7 +24,7 @@
</div>
@code {
public override string Name => "[Owner] [Theme] - Container1";
public override string Name => "Container";
private bool _title = true;
private string _classes = "container-fluid";

View File

@ -1,4 +1,4 @@
@namespace [Owner].Theme.[Theme]
@namespace Oqtane.Application.MyTheme
@inherits ModuleBase
@implements Oqtane.Interfaces.ISettingsControl
@inject ISettingService SettingService
@ -17,7 +17,7 @@
</div>
@code {
private string resourceType = "[Owner].Theme.[Theme].ContainerSettings, [Owner].Theme.[Theme].Client.Oqtane"; // for localization
private string resourceType = "Oqtane.Application.MyTheme.ContainerSettings, Oqtane.Application.Client.Oqtane"; // for localization
private string _title = "true";
protected override void OnInitialized()

View File

@ -0,0 +1,25 @@
using System.Collections.Generic;
using Oqtane.Models;
using Oqtane.Themes;
using Oqtane.Shared;
namespace Oqtane.Application.MyTheme
{
public class ThemeInfo : ITheme
{
public Oqtane.Models.Theme Theme => new Oqtane.Models.Theme
{
Name = "MyTheme",
Version = "1.0.0",
PackageName = "Oqtane.Application",
ThemeSettingsType = "Oqtane.Application.MyTheme.ThemeSettings, Oqtane.Application.Client.Oqtane",
ContainerSettingsType = "Oqtane.Application.MyTheme.ContainerSettings, Oqtane.Application.Client.Oqtane",
Resources = new List<Resource>()
{
new Stylesheet(Constants.BootstrapStylesheetUrl, Constants.BootstrapStylesheetIntegrity, "anonymous"),
new Stylesheet("~/Theme.css"),
new Script(Constants.BootstrapScriptUrl, Constants.BootstrapScriptIntegrity, "anonymous")
}
};
}
}

View File

@ -1,4 +1,4 @@
@namespace [Owner].Theme.[Theme]
@namespace Oqtane.Application.MyTheme
@inherits ThemeBase
@inject ISettingService SettingService
@ -95,7 +95,7 @@
</main>
@code {
public override string Name => "Theme1";
public override string Name => "MyTheme";
public override string Panes => PaneNames.Admin + ",Top Full Width,Top 100%,Left 50%,Right 50%,Left 33%,Center 33%,Right 33%,Left Outer 25%,Left Inner 25%,Right Inner 25%,Right Outer 25%,Left 25%,Center 50%,Right 25%,Left Sidebar 66%,Right Sidebar 33%,Left Sidebar 33%,Right Sidebar 66%,Bottom 100%,Bottom Full Width";

View File

@ -1,4 +1,4 @@
@namespace [Owner].Theme.[Theme]
@namespace Oqtane.Application.MyTheme
@inherits ModuleBase
@implements Oqtane.Interfaces.ISettingsControl
@inject ISettingService SettingService
@ -43,7 +43,7 @@
@code {
private int pageId = -1;
private string resourceType = "[Owner].Theme.[Theme].ThemeSettings, [Owner].Theme.[Theme].Client.Oqtane"; // for localization
private string resourceType = "Oqtane.Application.MyTheme.ThemeSettings, Oqtane.Application.Client.Oqtane"; // for localization
private string _scope = "page";
private string _login = "-";
private string _register = "-";

View File

@ -1,4 +1,4 @@
@using System
@using System
@using System.Linq
@using System.Collections.Generic
@using System.Net.Http
@ -10,6 +10,7 @@
@using Microsoft.Extensions.Localization
@using Microsoft.JSInterop
@using Oqtane
@using Oqtane.Models
@using Oqtane.Modules
@using Oqtane.Modules.Controls

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>Oqtane.Application.Template</id>
<version>6.2.0</version>
<title>Oqtane Application Template For Blazor</title>
<authors>Shaun Walker</authors>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<license type="expression">MIT</license>
<licenseUrl>https://licenses.nuget.org/MIT</licenseUrl>
<icon>icon.png</icon>
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
<description>Oqtane is an open source CMS and Application Framework that provides advanced functionality for developing web, mobile, and desktop applications on .NET. It leverages Blazor to compose a fully dynamic digital experience which can be hosted on Static Blazor, Blazor Server, Blazor WebAssembly, or Blazor Hybrid (via .NET MAUI).</description>
<language>en-US</language>
<tags>Web ASP.NET Blazor Oqtane Modular Multi-Tenant "Open Source" "SQL Server" MySQL PostgreSQL SQLite</tags>
<readme>README.md</readme>
<packageTypes>
<packageType name="Template" />
</packageTypes>
</metadata>
</package>

View File

@ -0,0 +1,33 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.12.35506.116 d17.12
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Oqtane.Application.Server", "Server\Oqtane.Application.Server.csproj", "{04B05448-788F-433D-92C0-FED35122D45A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Oqtane.Application.Client", "Client\Oqtane.Application.Client.csproj", "{AA8E58A1-CD09-4208-BF66-A8BB341FD669}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Oqtane.Application.Shared", "Shared\Oqtane.Application.Shared.csproj", "{18D73F73-D7BE-4388-85BA-FBD9AC96FCA2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{04B05448-788F-433D-92C0-FED35122D45A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{04B05448-788F-433D-92C0-FED35122D45A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{04B05448-788F-433D-92C0-FED35122D45A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{04B05448-788F-433D-92C0-FED35122D45A}.Release|Any CPU.Build.0 = Release|Any CPU
{AA8E58A1-CD09-4208-BF66-A8BB341FD669}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AA8E58A1-CD09-4208-BF66-A8BB341FD669}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AA8E58A1-CD09-4208-BF66-A8BB341FD669}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AA8E58A1-CD09-4208-BF66-A8BB341FD669}.Release|Any CPU.Build.0 = Release|Any CPU
{18D73F73-D7BE-4388-85BA-FBD9AC96FCA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{18D73F73-D7BE-4388-85BA-FBD9AC96FCA2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{18D73F73-D7BE-4388-85BA-FBD9AC96FCA2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{18D73F73-D7BE-4388-85BA-FBD9AC96FCA2}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,19 @@
# Oqtane Application Template
This is a Visual Studio Project Template designed for Oqtane development projects. This template relies on the native templating capabilities of the .NET Command Line Interface (CLI):
```
dotnet new install Oqtane.Application.Template
dotnet new oqtane-app -o MyCompany.MyProject
```
When using this approach you do not need to have a local copy of the oqtane.framework source code - you simply utilize Oqtane as a standard application dependency.
The solution contains an AppHost project which must be identified as the Startup project. It is responsible for loading the development environment and launching the Oqtane framework.
The solution also contains Build, Client, Server, and Shared folders which is where you you would implement your custom functionality. An example module and theme are included for reference, and you can add additional modules and themes within the same projects by following the standard Oqtane folder/namespace conventions.
*Known Issues*
- do not use the term "Oqtane" in your output name or else you will experience namespace conflicts

View File

@ -0,0 +1,3 @@
using Microsoft.Extensions.Localization;
[assembly: RootNamespace("Oqtane.Application.Server")]

View File

@ -5,36 +5,36 @@ using Microsoft.AspNetCore.Http;
using Oqtane.Shared;
using Oqtane.Enums;
using Oqtane.Infrastructure;
using [Owner].Module.[Module].Services;
using Oqtane.Application.Services;
using Oqtane.Controllers;
using System.Net;
using System.Threading.Tasks;
namespace [Owner].Module.[Module].Controllers
namespace Oqtane.Application.Controllers
{
[Route(ControllerRoutes.ApiRoute)]
public class [Module]Controller : ModuleControllerBase
public class MyModuleController : ModuleControllerBase
{
private readonly I[Module]Service _[Module]Service;
private readonly IMyModuleService _MyModuleService;
public [Module]Controller(I[Module]Service [Module]Service, ILogManager logger, IHttpContextAccessor accessor) : base(logger, accessor)
public MyModuleController(IMyModuleService MyModuleService, ILogManager logger, IHttpContextAccessor accessor) : base(logger, accessor)
{
_[Module]Service = [Module]Service;
_MyModuleService = MyModuleService;
}
// GET: api/<controller>?moduleid=x
[HttpGet]
[Authorize(Policy = PolicyNames.ViewModule)]
public async Task<IEnumerable<Models.[Module]>> Get(string moduleid)
public async Task<IEnumerable<Models.MyModule>> Get(string moduleid)
{
int ModuleId;
if (int.TryParse(moduleid, out ModuleId) && IsAuthorizedEntityId(EntityNames.Module, ModuleId))
{
return await _[Module]Service.Get[Module]sAsync(ModuleId);
return await _MyModuleService.GetMyModulesAsync(ModuleId);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized [Module] Get Attempt {ModuleId}", moduleid);
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized MyModule Get Attempt {ModuleId}", moduleid);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
return null;
}
@ -43,16 +43,16 @@ namespace [Owner].Module.[Module].Controllers
// GET api/<controller>/5
[HttpGet("{id}/{moduleid}")]
[Authorize(Policy = PolicyNames.ViewModule)]
public async Task<Models.[Module]> Get(int id, int moduleid)
public async Task<Models.MyModule> Get(int id, int moduleid)
{
Models.[Module] [Module] = await _[Module]Service.Get[Module]Async(id, moduleid);
if ([Module] != null && IsAuthorizedEntityId(EntityNames.Module, [Module].ModuleId))
Models.MyModule MyModule = await _MyModuleService.GetMyModuleAsync(id, moduleid);
if (MyModule != null && IsAuthorizedEntityId(EntityNames.Module, MyModule.ModuleId))
{
return [Module];
return MyModule;
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized [Module] Get Attempt {[Module]Id} {ModuleId}", id, moduleid);
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized MyModule Get Attempt {MyModuleId} {ModuleId}", id, moduleid);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
return null;
}
@ -61,37 +61,37 @@ namespace [Owner].Module.[Module].Controllers
// POST api/<controller>
[HttpPost]
[Authorize(Policy = PolicyNames.EditModule)]
public async Task<Models.[Module]> Post([FromBody] Models.[Module] [Module])
public async Task<Models.MyModule> Post([FromBody] Models.MyModule MyModule)
{
if (ModelState.IsValid && IsAuthorizedEntityId(EntityNames.Module, [Module].ModuleId))
if (ModelState.IsValid && IsAuthorizedEntityId(EntityNames.Module, MyModule.ModuleId))
{
[Module] = await _[Module]Service.Add[Module]Async([Module]);
MyModule = await _MyModuleService.AddMyModuleAsync(MyModule);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized [Module] Post Attempt {[Module]}", [Module]);
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized MyModule Post Attempt {MyModule}", MyModule);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
[Module] = null;
MyModule = null;
}
return [Module];
return MyModule;
}
// PUT api/<controller>/5
[HttpPut("{id}")]
[Authorize(Policy = PolicyNames.EditModule)]
public async Task<Models.[Module]> Put(int id, [FromBody] Models.[Module] [Module])
public async Task<Models.MyModule> Put(int id, [FromBody] Models.MyModule MyModule)
{
if (ModelState.IsValid && [Module].[Module]Id == id && IsAuthorizedEntityId(EntityNames.Module, [Module].ModuleId))
if (ModelState.IsValid && MyModule.MyModuleId == id && IsAuthorizedEntityId(EntityNames.Module, MyModule.ModuleId))
{
[Module] = await _[Module]Service.Update[Module]Async([Module]);
MyModule = await _MyModuleService.UpdateMyModuleAsync(MyModule);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized [Module] Put Attempt {[Module]}", [Module]);
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized MyModule Put Attempt {MyModule}", MyModule);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
[Module] = null;
MyModule = null;
}
return [Module];
return MyModule;
}
// DELETE api/<controller>/5
@ -99,14 +99,14 @@ namespace [Owner].Module.[Module].Controllers
[Authorize(Policy = PolicyNames.EditModule)]
public async Task Delete(int id, int moduleid)
{
Models.[Module] [Module] = await _[Module]Service.Get[Module]Async(id, moduleid);
if ([Module] != null && IsAuthorizedEntityId(EntityNames.Module, [Module].ModuleId))
Models.MyModule MyModule = await _MyModuleService.GetMyModuleAsync(id, moduleid);
if (MyModule != null && IsAuthorizedEntityId(EntityNames.Module, MyModule.ModuleId))
{
await _[Module]Service.Delete[Module]Async(id, [Module].ModuleId);
await _MyModuleService.DeleteMyModuleAsync(id, MyModule.ModuleId);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized [Module] Delete Attempt {[Module]Id} {ModuleId}", id, moduleid);
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized v Delete Attempt {MyModuleId} {ModuleId}", id, moduleid);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
}
}

View File

@ -0,0 +1,87 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using Oqtane.Modules;
using Oqtane.Models;
using Oqtane.Infrastructure;
using Oqtane.Interfaces;
using Oqtane.Enums;
using Oqtane.Repository;
using Oqtane.Application.Repository;
using System.Threading.Tasks;
namespace Oqtane.Application.Manager
{
public class MyModuleManager : MigratableModuleBase, IInstallable, IPortable, ISearchable
{
private readonly IMyModuleRepository _MyModuleRepository;
private readonly IDBContextDependencies _DBContextDependencies;
public MyModuleManager(IMyModuleRepository MyModuleRepository, IDBContextDependencies DBContextDependencies)
{
_MyModuleRepository = MyModuleRepository;
_DBContextDependencies = DBContextDependencies;
}
public bool Install(Tenant tenant, string version)
{
return Migrate(new Context(_DBContextDependencies), tenant, MigrationType.Up);
}
public bool Uninstall(Tenant tenant)
{
return Migrate(new Context(_DBContextDependencies), tenant, MigrationType.Down);
}
public string ExportModule(Module module)
{
string content = "";
List<Models.MyModule> MyModules = _MyModuleRepository.GetMyModules(module.ModuleId).ToList();
if (MyModules != null)
{
content = JsonSerializer.Serialize(MyModules);
}
return content;
}
public void ImportModule(Module module, string content, string version)
{
List<Models.MyModule> MyModules = null;
if (!string.IsNullOrEmpty(content))
{
MyModules = JsonSerializer.Deserialize<List<Models.MyModule>>(content);
}
if (MyModules != null)
{
foreach(var Task in MyModules)
{
_MyModuleRepository.AddMyModule(new Models.MyModule { ModuleId = module.ModuleId, Name = Task.Name });
}
}
}
public Task<List<SearchContent>> GetSearchContentsAsync(PageModule pageModule, DateTime lastIndexedOn)
{
var searchContentList = new List<SearchContent>();
foreach (var MyModule in _MyModuleRepository.GetMyModules(pageModule.ModuleId))
{
if (MyModule.ModifiedOn >= lastIndexedOn)
{
searchContentList.Add(new SearchContent
{
EntityName = "MyModule",
EntityId = MyModule.MyModuleId.ToString(),
Title = MyModule.Name,
Body = MyModule.Name,
ContentModifiedBy = MyModule.ModifiedBy,
ContentModifiedOn = MyModule.ModifiedOn
});
}
}
return Task.FromResult(searchContentList);
}
}
}

View File

@ -2,13 +2,13 @@ using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Oqtane.Databases.Interfaces;
using Oqtane.Migrations;
using [Owner].Module.[Module].Migrations.EntityBuilders;
using [Owner].Module.[Module].Repository;
using Oqtane.Application.Migrations.EntityBuilders;
using Oqtane.Application.Repository;
namespace [Owner].Module.[Module].Migrations
namespace Oqtane.Application.Migrations
{
[DbContext(typeof([Module]Context))]
[Migration("[Owner].Module.[Module].01.00.00.00")]
[DbContext(typeof(Context))]
[Migration("Oqtane.Application.01.00.00.00")]
public class InitializeModule : MultiDatabaseMigration
{
public InitializeModule(IDatabase database) : base(database)
@ -17,14 +17,14 @@ namespace [Owner].Module.[Module].Migrations
protected override void Up(MigrationBuilder migrationBuilder)
{
var entityBuilder = new [Module]EntityBuilder(migrationBuilder, ActiveDatabase);
entityBuilder.Create();
var myModuleEntityBuilder = new MyModuleEntityBuilder(migrationBuilder, ActiveDatabase);
myModuleEntityBuilder.Create();
}
protected override void Down(MigrationBuilder migrationBuilder)
{
var entityBuilder = new [Module]EntityBuilder(migrationBuilder, ActiveDatabase);
entityBuilder.Drop();
var myModuleEntityBuilder = new MyModuleEntityBuilder(migrationBuilder, ActiveDatabase);
myModuleEntityBuilder.Drop();
}
}
}

View File

@ -5,31 +5,31 @@ using Oqtane.Databases.Interfaces;
using Oqtane.Migrations;
using Oqtane.Migrations.EntityBuilders;
namespace [Owner].Module.[Module].Migrations.EntityBuilders
namespace Oqtane.Application.Migrations.EntityBuilders
{
public class [Module]EntityBuilder : AuditableBaseEntityBuilder<[Module]EntityBuilder>
public class MyModuleEntityBuilder : AuditableBaseEntityBuilder<MyModuleEntityBuilder>
{
private const string _entityTableName = "[Owner][Module]";
private readonly PrimaryKey<[Module]EntityBuilder> _primaryKey = new("PK_[Owner][Module]", x => x.[Module]Id);
private readonly ForeignKey<[Module]EntityBuilder> _moduleForeignKey = new("FK_[Owner][Module]_Module", x => x.ModuleId, "Module", "ModuleId", ReferentialAction.Cascade);
private const string _entityTableName = "MyModule";
private readonly PrimaryKey<MyModuleEntityBuilder> _primaryKey = new("PK_MyModule", x => x.MyModuleId);
private readonly ForeignKey<MyModuleEntityBuilder> _moduleForeignKey = new("FK_MyModule_Module", x => x.ModuleId, "Module", "ModuleId", ReferentialAction.Cascade);
public [Module]EntityBuilder(MigrationBuilder migrationBuilder, IDatabase database) : base(migrationBuilder, database)
public MyModuleEntityBuilder(MigrationBuilder migrationBuilder, IDatabase database) : base(migrationBuilder, database)
{
EntityTableName = _entityTableName;
PrimaryKey = _primaryKey;
ForeignKeys.Add(_moduleForeignKey);
}
protected override [Module]EntityBuilder BuildTable(ColumnsBuilder table)
protected override MyModuleEntityBuilder BuildTable(ColumnsBuilder table)
{
[Module]Id = AddAutoIncrementColumn(table,"[Module]Id");
MyModuleId = AddAutoIncrementColumn(table, "MyModuleId");
ModuleId = AddIntegerColumn(table,"ModuleId");
Name = AddMaxStringColumn(table,"Name");
AddAuditableColumns(table);
return this;
}
public OperationBuilder<AddColumnOperation> [Module]Id { get; set; }
public OperationBuilder<AddColumnOperation> MyModuleId { get; set; }
public OperationBuilder<AddColumnOperation> ModuleId { get; set; }
public OperationBuilder<AddColumnOperation> Name { get; set; }
}

View File

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Version>1.0.0</Version>
<AssemblyName>Oqtane.Application.Server.Oqtane</AssemblyName>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Client\Oqtane.Application.Client.csproj" />
<ProjectReference Include="..\Shared\Oqtane.Application.Shared.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Oqtane.Server" Version="6.2.0" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,42 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Oqtane.Infrastructure;
using Microsoft.Extensions.DependencyInjection;
namespace Oqtane.Application.Server
{
public class Program
{
public static void Main(string[] args)
{
// defer server startup to Oqtane - do not modify
var host = BuildWebHost(args);
var databaseManager = host.Services.GetService<IDatabaseManager>();
var install = databaseManager.Install();
if (!string.IsNullOrEmpty(install.Message))
{
var filelogger = host.Services.GetRequiredService<ILogger<Program>>();
if (filelogger != null)
{
filelogger.LogError($"[Oqtane.Application.Server.Program.Main] {install.Message}");
}
}
else
{
host.Run();
}
}
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseConfiguration(new ConfigurationBuilder()
.AddCommandLine(args)
.AddEnvironmentVariables()
.Build())
.UseStartup<Startup>()
.ConfigureLocalizationSettings()
.Build();
}
}

View File

@ -0,0 +1,25 @@
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
"applicationUrl": "http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
"applicationUrl": "https://localhost:5001;http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@ -0,0 +1,24 @@
using Microsoft.EntityFrameworkCore;
using Oqtane.Modules;
using Oqtane.Repository;
using Oqtane.Repository.Databases.Interfaces;
namespace Oqtane.Application.Repository
{
public class Context : DBContextBase, ITransientService, IMultiDatabase
{
public virtual DbSet<Models.MyModule> MyModule { get; set; }
public Context(IDBContextDependencies DBContextDependencies) : base(DBContextDependencies)
{
// ContextBase handles multi-tenant database connections
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<Models.MyModule>().ToTable(ActiveDatabase.RewriteName("MyModule"));
}
}
}

View File

@ -0,0 +1,75 @@
using Microsoft.EntityFrameworkCore;
using System.Linq;
using System.Collections.Generic;
using Oqtane.Modules;
namespace Oqtane.Application.Repository
{
public interface IMyModuleRepository
{
IEnumerable<Models.MyModule> GetMyModules(int ModuleId);
Models.MyModule GetMyModule(int MyModuleId);
Models.MyModule GetMyModule(int MyModuleId, bool tracking);
Models.MyModule AddMyModule(Models.MyModule MyModule);
Models.MyModule UpdateMyModule(Models.MyModule MyModule);
void DeleteMyModule(int MyModuleId);
}
public class MyModuleRepository : IMyModuleRepository, ITransientService
{
private readonly IDbContextFactory<Context> _factory;
public MyModuleRepository(IDbContextFactory<Context> factory)
{
_factory = factory;
}
public IEnumerable<Models.MyModule> GetMyModules(int ModuleId)
{
using var db = _factory.CreateDbContext();
return db.MyModule.Where(item => item.ModuleId == ModuleId).ToList();
}
public Models.MyModule GetMyModule(int MyModuleId)
{
return GetMyModule(MyModuleId, true);
}
public Models.MyModule GetMyModule(int MyModuleId, bool tracking)
{
using var db = _factory.CreateDbContext();
if (tracking)
{
return db.MyModule.Find(MyModuleId);
}
else
{
return db.MyModule.AsNoTracking().FirstOrDefault(item => item.MyModuleId == MyModuleId);
}
}
public Models.MyModule AddMyModule(Models.MyModule MyModule)
{
using var db = _factory.CreateDbContext();
db.MyModule.Add(MyModule);
db.SaveChanges();
return MyModule;
}
public Models.MyModule UpdateMyModule(Models.MyModule MyModule)
{
using var db = _factory.CreateDbContext();
db.Entry(MyModule).State = EntityState.Modified;
db.SaveChanges();
return MyModule;
}
public void DeleteMyModule(int MyModuleId)
{
using var db = _factory.CreateDbContext();
Models.MyModule MyModule = db.MyModule.Find(MyModuleId);
db.MyModule.Remove(MyModule);
db.SaveChanges();
}
}
}

View File

@ -7,93 +7,93 @@ using Oqtane.Infrastructure;
using Oqtane.Models;
using Oqtane.Security;
using Oqtane.Shared;
using [Owner].Module.[Module].Repository;
using Oqtane.Application.Repository;
namespace [Owner].Module.[Module].Services
namespace Oqtane.Application.Services
{
public class Server[Module]Service : I[Module]Service
public class ServerMyModuleService : IMyModuleService
{
private readonly I[Module]Repository _[Module]Repository;
private readonly IMyModuleRepository _MyModuleRepository;
private readonly IUserPermissions _userPermissions;
private readonly ILogManager _logger;
private readonly IHttpContextAccessor _accessor;
private readonly Alias _alias;
public Server[Module]Service(I[Module]Repository [Module]Repository, IUserPermissions userPermissions, ITenantManager tenantManager, ILogManager logger, IHttpContextAccessor accessor)
public ServerMyModuleService(IMyModuleRepository MyModuleRepository, IUserPermissions userPermissions, ITenantManager tenantManager, ILogManager logger, IHttpContextAccessor accessor)
{
_[Module]Repository = [Module]Repository;
_MyModuleRepository = MyModuleRepository;
_userPermissions = userPermissions;
_logger = logger;
_accessor = accessor;
_alias = tenantManager.GetAlias();
}
public Task<List<Models.[Module]>> Get[Module]sAsync(int ModuleId)
public Task<List<Models.MyModule>> GetMyModulesAsync(int ModuleId)
{
if (_userPermissions.IsAuthorized(_accessor.HttpContext.User, _alias.SiteId, EntityNames.Module, ModuleId, PermissionNames.View))
{
return Task.FromResult(_[Module]Repository.Get[Module]s(ModuleId).ToList());
return Task.FromResult(_MyModuleRepository.GetMyModules(ModuleId).ToList());
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized [Module] Get Attempt {ModuleId}", ModuleId);
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized MyModule Get Attempt {ModuleId}", ModuleId);
return null;
}
}
public Task<Models.[Module]> Get[Module]Async(int [Module]Id, int ModuleId)
public Task<Models.MyModule> GetMyModuleAsync(int MyModuleId, int ModuleId)
{
if (_userPermissions.IsAuthorized(_accessor.HttpContext.User, _alias.SiteId, EntityNames.Module, ModuleId, PermissionNames.View))
{
return Task.FromResult(_[Module]Repository.Get[Module]([Module]Id));
return Task.FromResult(_MyModuleRepository.GetMyModule(MyModuleId));
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized [Module] Get Attempt {[Module]Id} {ModuleId}", [Module]Id, ModuleId);
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized MyModule Get Attempt {TaskId} {ModuleId}", MyModuleId, ModuleId);
return null;
}
}
public Task<Models.[Module]> Add[Module]Async(Models.[Module] [Module])
public Task<Models.MyModule> AddMyModuleAsync(Models.MyModule MyModule)
{
if (_userPermissions.IsAuthorized(_accessor.HttpContext.User, _alias.SiteId, EntityNames.Module, [Module].ModuleId, PermissionNames.Edit))
if (_userPermissions.IsAuthorized(_accessor.HttpContext.User, _alias.SiteId, EntityNames.Module, MyModule.ModuleId, PermissionNames.Edit))
{
[Module] = _[Module]Repository.Add[Module]([Module]);
_logger.Log(LogLevel.Information, this, LogFunction.Create, "[Module] Added {[Module]}", [Module]);
MyModule = _MyModuleRepository.AddMyModule(MyModule);
_logger.Log(LogLevel.Information, this, LogFunction.Create, "MyModule Added {MyModule}", MyModule);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized [Module] Add Attempt {[Module]}", [Module]);
[Module] = null;
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized MyModule Add Attempt {MyModule}", MyModule);
MyModule = null;
}
return Task.FromResult([Module]);
return Task.FromResult(MyModule);
}
public Task<Models.[Module]> Update[Module]Async(Models.[Module] [Module])
public Task<Models.MyModule> UpdateMyModuleAsync(Models.MyModule MyModule)
{
if (_userPermissions.IsAuthorized(_accessor.HttpContext.User, _alias.SiteId, EntityNames.Module, [Module].ModuleId, PermissionNames.Edit))
if (_userPermissions.IsAuthorized(_accessor.HttpContext.User, _alias.SiteId, EntityNames.Module, MyModule.ModuleId, PermissionNames.Edit))
{
[Module] = _[Module]Repository.Update[Module]([Module]);
_logger.Log(LogLevel.Information, this, LogFunction.Update, "[Module] Updated {[Module]}", [Module]);
MyModule = _MyModuleRepository.UpdateMyModule(MyModule);
_logger.Log(LogLevel.Information, this, LogFunction.Update, "MyModule Updated {MyModule}", MyModule);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized [Module] Update Attempt {[Module]}", [Module]);
[Module] = null;
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized MyModule Update Attempt {MyModule}", MyModule);
MyModule = null;
}
return Task.FromResult([Module]);
return Task.FromResult(MyModule);
}
public Task Delete[Module]Async(int [Module]Id, int ModuleId)
public Task DeleteMyModuleAsync(int MyModuleId, int ModuleId)
{
if (_userPermissions.IsAuthorized(_accessor.HttpContext.User, _alias.SiteId, EntityNames.Module, ModuleId, PermissionNames.Edit))
{
_[Module]Repository.Delete[Module]([Module]Id);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "[Module] Deleted {[Module]Id}", [Module]Id);
_MyModuleRepository.DeleteMyModule(MyModuleId);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "MyModule Deleted {MyModuleId}", MyModuleId);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized [Module] Delete Attempt {[Module]Id} {ModuleId}", [Module]Id, ModuleId);
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized MyModule Delete Attempt {MyModuleId} {ModuleId}", MyModuleId, ModuleId);
}
return Task.CompletedTask;
}

View File

@ -0,0 +1,45 @@
using System;
using System.IO;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Oqtane.Extensions;
using Oqtane.Infrastructure;
using Oqtane.Shared;
using Microsoft.AspNetCore.Cors.Infrastructure;
namespace Oqtane.Application.Server
{
public class Startup
{
private readonly IConfigurationRoot _configuration;
private readonly IWebHostEnvironment _environment;
public Startup(IWebHostEnvironment environment)
{
AppDomain.CurrentDomain.SetData(Constants.DataDirectory, Path.Combine(environment.ContentRootPath, "Data"));
var builder = new ConfigurationBuilder()
.SetBasePath(environment.ContentRootPath)
.AddJsonFile("appsettings.json", false, true)
.AddJsonFile($"appsettings.{environment.EnvironmentName}.json", true, true)
.AddEnvironmentVariables();
_configuration = builder.Build();
_environment = environment;
}
public void ConfigureServices(IServiceCollection services)
{
// defer server startup to Oqtane - do not modify
services.AddOqtane(_configuration, _environment);
}
public void Configure(IApplicationBuilder app, IConfigurationRoot configuration, IWebHostEnvironment environment, ICorsService corsService, ICorsPolicyProvider corsPolicyProvider, ISyncManager sync)
{
// defer server startup to Oqtane - do not modify
app.UseOqtane(configuration, environment, corsService, corsPolicyProvider, sync);
}
}
}

View File

@ -2,10 +2,10 @@ using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Oqtane.Infrastructure;
using [Owner].Module.[Module].Repository;
using [Owner].Module.[Module].Services;
using Oqtane.Application.Repository;
using Oqtane.Application.Services;
namespace [Owner].Module.[Module].Startup
namespace Oqtane.Application.Startup
{
public class ServerStartup : IServerStartup
{
@ -21,8 +21,8 @@ namespace [Owner].Module.[Module].Startup
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<I[Module]Service, Server[Module]Service>();
services.AddDbContextFactory<[Module]Context>(opt => { }, ServiceLifetime.Transient);
services.AddTransient<IMyModuleService, ServerMyModuleService>();
services.AddDbContextFactory<Context>(opt => { }, ServiceLifetime.Transient);
}
}
}

View File

@ -1,5 +1,5 @@
{
"RenderMode": "Interactive",
"RenderMode": "Static",
"Runtime": "Server",
"Database": {
"DefaultDBType": ""
@ -22,27 +22,42 @@
{
"Name": "LocalDB",
"ControlType": "Oqtane.Installer.Controls.LocalDBConfig, Oqtane.Client",
"DBType": "Oqtane.Database.SqlServer.SqlServerDatabase, Oqtane.Database.SqlServer"
"DBType": "Oqtane.Database.SqlServer.SqlServerDatabase, Oqtane.Server"
},
{
"Name": "SQL Server",
"ControlType": "Oqtane.Installer.Controls.SqlServerConfig, Oqtane.Client",
"DBType": "Oqtane.Database.SqlServer.SqlServerDatabase, Oqtane.Database.SqlServer"
"DBType": "Oqtane.Database.SqlServer.SqlServerDatabase, Oqtane.Server"
},
{
"Name": "SQLite",
"ControlType": "Oqtane.Installer.Controls.SqliteConfig, Oqtane.Client",
"DBType": "Oqtane.Database.Sqlite.SqliteDatabase, Oqtane.Database.Sqlite"
"DBType": "Oqtane.Database.Sqlite.SqliteDatabase, Oqtane.Server"
},
{
"Name": "MySQL",
"ControlType": "Oqtane.Installer.Controls.MySQLConfig, Oqtane.Client",
"DBType": "Oqtane.Database.MySQL.MySQLDatabase, Oqtane.Database.MySQL"
"DBType": "Oqtane.Database.MySQL.MySQLDatabase, Oqtane.Server"
},
{
"Name": "PostgreSQL",
"ControlType": "Oqtane.Installer.Controls.PostgreSQLConfig, Oqtane.Client",
"DBType": "Oqtane.Database.PostgreSQL.PostgreSQLDatabase, Oqtane.Database.PostgreSQL"
"DBType": "Oqtane.Database.PostgreSQL.PostgreSQLDatabase, Oqtane.Server"
},
{
"Name": "Azure SQL",
"ControlType": "Oqtane.Installer.Controls.AzureSqlConfig, Oqtane.Client",
"DBType": "Oqtane.Database.SqlServer.SqlServerDatabase, Oqtane.Server"
}
]
],
"Logging": {
"FileLogger": {
"LogLevel": {
"Default": "Error"
}
},
"LogLevel": {
"Default": "Information"
}
}
}

View File

@ -0,0 +1,5 @@
/* Module Script */
var App = App || {};
App.MyModule = {
};

View File

@ -75,6 +75,10 @@ app {
color: gray;
}
.app-moduleactions .dropdown-menu {
z-index: 9999;
}
.app-moduleactions .dropdown-submenu {
position: relative;
}
@ -274,11 +278,11 @@ app {
}
/* cookie consent */
.gdpr-consent-bar .btn-show{
.gdpr-consent-bar .btn-show {
bottom: -3px;
left: 5px;
}
.gdpr-consent-bar .btn-hide{
.gdpr-consent-bar .btn-hide {
top: 0;
right: 5px;
}

View File

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 54 KiB

View File

@ -0,0 +1,22 @@
.rz-text-editor {
outline: none !important;
}
.rz-html-editor-dropdown-items,
.rz-popup,
.rz-editor-dialog-wrapper {
z-index: 9999 !important;
}
.rz-html-editor-dropdown-items .rz-html-editor-dropdown-item,
.rz-html-editor-dropdown-items .rz-html-editor-dropdown-item > * {
color: var(--rz-editor-button-color);
}
.rz-text-editor .rz-html-editor-dropdown .rz-html-editor-dropdown-value,
.rz-text-editor .rz-html-editor-dropdown .rz-html-editor-dropdown-trigger,
.rz-text-editor .rz-html-editor-colorpicker .rz-html-editor-color {
color: var(--rz-editor-button-color);
}
.rz-text-editor .rz-colorpicker.rz-state-disabled {
border: none !important;
}

View File

Before

Width:  |  Height:  |  Size: 318 B

After

Width:  |  Height:  |  Size: 318 B

View File

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

Before

Width:  |  Height:  |  Size: 427 B

After

Width:  |  Height:  |  Size: 427 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 875 B

View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

Before

Width:  |  Height:  |  Size: 801 B

After

Width:  |  Height:  |  Size: 801 B

View File

Before

Width:  |  Height:  |  Size: 7.9 KiB

After

Width:  |  Height:  |  Size: 7.9 KiB

View File

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 7.8 KiB

View File

Before

Width:  |  Height:  |  Size: 177 B

After

Width:  |  Height:  |  Size: 177 B

View File

Before

Width:  |  Height:  |  Size: 438 B

After

Width:  |  Height:  |  Size: 438 B

View File

@ -311,7 +311,7 @@ Oqtane.Interop = {
}
return files;
},
uploadFiles: async function (posturl, folder, id, antiforgerytoken, jwt, chunksize) {
uploadFiles: async function (posturl, folder, id, antiforgerytoken, jwt, chunksize, anonymizeuploadfilenames) {
var success = true;
var fileinput = document.getElementById('FileInput_' + id);
var progressinfo = document.getElementById('ProgressInfo_' + id);
@ -344,16 +344,22 @@ Oqtane.Interop = {
const totalParts = Math.ceil(file.size / chunkSize);
let partCount = 0;
let filename = file.name;
if (anonymizeuploadfilenames) {
filename = crypto.randomUUID() + '.' + filename.split('.').pop();
}
const uploadPart = () => {
const start = partCount * chunkSize;
const end = Math.min(start + chunkSize, file.size);
const chunk = file.slice(start, end);
return new Promise((resolve, reject) => {
let formdata = new FormData();
formdata.append('__RequestVerificationToken', antiforgerytoken);
formdata.append('folder', folder);
formdata.append('formfile', chunk, file.name);
formdata.append('formfile', chunk, filename);
var credentials = 'same-origin';
var headers = new Headers();

View File

@ -0,0 +1,47 @@
var Oqtane = Oqtane || {};
Oqtane.RadzenTextEditor = {
initialize: function (editor) {
if (typeof Radzen.openPopup === "function" && Radzen.openPopup !== Oqtane.RadzenTextEditor.openPopup) {
Oqtane.RadzenTextEditor.radzenOpenPopup = Radzen.openPopup;
Radzen.openPopup = Oqtane.RadzenTextEditor.openPopup;
}
},
openPopup: function () {
Oqtane.RadzenTextEditor.radzenOpenPopup.apply(this, arguments);
var id = arguments[1];
var popup = document.getElementById(id);
if (popup) {
Oqtane.RadzenTextEditor.updateButtonStyles(popup);
}
},
setBackgroundColor: function (editor, color) {
editor.getElementsByClassName("rz-html-editor-content")[0].style.backgroundColor = color;
},
updateDialogLayout: function (editor) {
var dialogs = editor.parentElement.getElementsByClassName('rz-dialog-wrapper');
for (var dialog of dialogs) {
document.body.appendChild(dialog);
dialog.classList.add('rz-editor-dialog-wrapper', 'text-dark');
this.updateButtonStyles(dialog);
}
},
updateButtonStyles: function (parent) {
var primaryBtns = parent.getElementsByClassName('rz-primary');
if (primaryBtns) {
for (var btn of primaryBtns) {
btn.classList.remove('rz-button', 'rz-primary');
btn.classList.add('btn', 'btn-primary');
}
}
var secondaryBtns = parent.getElementsByClassName('rz-secondary');
if (secondaryBtns) {
for (var btn of secondaryBtns) {
btn.classList.remove('rz-button', 'rz-secondary');
btn.classList.add('btn', 'btn-secondary');
}
}
}
}

View File

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 8.0 KiB

View File

Before

Width:  |  Height:  |  Size: 92 KiB

After

Width:  |  Height:  |  Size: 92 KiB

View File

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 72 KiB

View File

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

View File

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

Before

Width:  |  Height:  |  Size: 92 KiB

After

Width:  |  Height:  |  Size: 92 KiB

View File

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

@ -1,15 +1,13 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Oqtane.Models;
namespace [Owner].Module.[Module].Models
namespace Oqtane.Application.Models
{
[Table("[Owner][Module]")]
public class [Module] : IAuditable
public class MyModule : IAuditable
{
[Key]
public int [Module]Id { get; set; }
public int MyModuleId { get; set; }
public int ModuleId { get; set; }
public string Name { get; set; }

View File

@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Version>1.0.0</Version>
<AssemblyName>Oqtane.Application.Shared.Oqtane</AssemblyName>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Oqtane.Shared" Version="6.2.0" />
</ItemGroup>
</Project>

View File

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

@ -1,8 +1,10 @@
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.Extensions.Localization;
using Oqtane.Interfaces;
using Oqtane.Providers;
using Oqtane.Services;
using Oqtane.Shared;
using Radzen;
namespace Microsoft.Extensions.DependencyInjection
{
@ -23,7 +25,7 @@ namespace Microsoft.Extensions.DependencyInjection
services.AddScoped<SiteState>();
services.AddScoped<IInstallationService, InstallationService>();
services.AddScoped<IModuleDefinitionService, ModuleDefinitionService>();
services.AddScoped<IThemeService, ThemeService>();
services.AddScoped<IThemeService, Oqtane.Services.ThemeService>();
services.AddScoped<IAliasService, AliasService>();
services.AddScoped<ITenantService, TenantService>();
services.AddScoped<ISiteService, SiteService>();
@ -39,7 +41,7 @@ namespace Microsoft.Extensions.DependencyInjection
services.AddScoped<ILogService, LogService>();
services.AddScoped<IJobService, JobService>();
services.AddScoped<IJobLogService, JobLogService>();
services.AddScoped<INotificationService, NotificationService>();
services.AddScoped<INotificationService, Oqtane.Services.NotificationService>();
services.AddScoped<IFolderService, FolderService>();
services.AddScoped<IFileService, FileService>();
services.AddScoped<ISiteTemplateService, SiteTemplateService>();
@ -53,12 +55,18 @@ namespace Microsoft.Extensions.DependencyInjection
services.AddScoped<ISyncService, SyncService>();
services.AddScoped<ILocalizationCookieService, LocalizationCookieService>();
services.AddScoped<ICookieConsentService, CookieConsentService>();
services.AddScoped<IOutputCacheService, OutputCacheService>();
services.AddScoped<ITimeZoneService, TimeZoneService>();
services.AddScoped<IOutputCacheService, OutputCacheService>();
// providers
services.AddScoped<ITextEditor, Oqtane.Modules.Controls.QuillJSTextEditor>();
services.AddScoped<ITextEditor, Oqtane.Modules.Controls.TextAreaTextEditor>();
services.AddScoped<ITextEditor, Oqtane.Modules.Controls.RadzenTextEditor>();
services.AddRadzenComponents();
var localizer = services.BuildServiceProvider().GetService<IStringLocalizer<Oqtane.Modules.Controls.RadzenTextEditor>>();
Oqtane.Modules.Controls.RadzenEditorDefinitions.Localizer = localizer;
return services;
}

View File

@ -14,7 +14,7 @@
<div class="container">
<div class="row">
<div class="mx-auto text-center">
<img src="oqtane-black.png" />
<img src="installer-logo.png" />
<div style="font-weight: bold">@SharedLocalizer["Version"] @Constants.Version (.NET @Environment.Version.Major)</div>
</div>
</div>
@ -182,7 +182,7 @@
}
else
{
_databaseName = "LocalDB";
_databaseName = Constants.DefaultDBName;
}
LoadDatabaseConfigComponent();
@ -269,8 +269,8 @@
SiteName = Constants.DefaultSite,
Register = _register,
SiteTemplate = _template,
RenderMode = RenderModes.Static,
Runtime = Runtimes.Server
RenderMode = "", // provided by appsettings.json
Runtime = "" // provided by appsettings.json
};
var installation = await InstallationService.Install(config);

View File

@ -56,7 +56,7 @@
<input id="starting" type="date" class="form-control" @bind="@_startDate" />
</div>
<div class="col">
<input id="starting" type="time" class="form-control" placeholder="hh:mm" @bind="@_startTime" />
<input id="starting" type="time" class="form-control" @bind="@_startTime" placeholder="hh:mm" required="@(_startDate.HasValue)" />
</div>
</div>
</div>
@ -69,7 +69,7 @@
<input id="ending" type="date" class="form-control" @bind="@_endDate" />
</div>
<div class="col">
<input id="ending" type="time" class="form-control" placeholder="hh:mm" @bind="@_endTime" />
<input id="ending" type="time" class="form-control" placeholder="hh:mm" @bind="@_endTime" required="@(_endDate.HasValue)" />
</div>
</div>
</div>
@ -82,7 +82,7 @@
<input id="next" type="date" class="form-control" @bind="@_nextDate" />
</div>
<div class="col">
<input id="next" type="time" class="form-control" placeholder="hh:mm" @bind="@_nextTime" />
<input id="next" type="time" class="form-control" placeholder="hh:mm" @bind="@_nextTime" required="@(_nextDate.HasValue)" />
</div>
</div>
</div>
@ -176,10 +176,18 @@
{
job.Interval = int.Parse(_interval);
}
job.StartDate = LocalToUtc(_startDate.Value.Date.Add(_startTime.Value.TimeOfDay));
job.EndDate = LocalToUtc(_endDate.Value.Date.Add(_endTime.Value.TimeOfDay));
job.RetentionHistory = int.Parse(_retentionHistory);
job.NextExecution = LocalToUtc(_nextDate.Value.Date.Add(_nextTime.Value.TimeOfDay));
job.StartDate = _startDate.HasValue && _startTime.HasValue
? LocalToUtc(_startDate.GetValueOrDefault().Date.Add(_startTime.GetValueOrDefault().TimeOfDay))
: null;
job.EndDate = _endDate.HasValue && _endTime.HasValue
? LocalToUtc(_endDate.GetValueOrDefault().Date.Add(_endTime.GetValueOrDefault().TimeOfDay))
: null;
job.NextExecution = _nextDate.HasValue && _nextTime.HasValue
? LocalToUtc(_nextDate.GetValueOrDefault().Date.Add(_nextTime.GetValueOrDefault().TimeOfDay))
: null;
job.RetentionHistory = int.Parse(_retentionHistory);
try
{
@ -198,5 +206,4 @@
AddModuleMessage(Localizer["Message.Required.JobInfo"], MessageType.Warning);
}
}
}

View File

@ -116,11 +116,19 @@ else
{
try
{
await JobService.StartJobAsync(jobId);
await logger.LogInformation("Job Started {JobId}", jobId);
AddModuleMessage(Localizer["Message.Job.Start"], MessageType.Success);
_jobs = await JobService.GetJobsAsync();
StateHasChanged();
Job _job = await JobService.GetJobAsync(jobId);
if (!_job.IsEnabled)
{
AddModuleMessage(Localizer["Message.Job.Disabled"], MessageType.Warning);
}
else
{
await JobService.StartJobAsync(jobId);
await logger.LogInformation("Job Started {JobId}", jobId);
AddModuleMessage(Localizer["Message.Job.Start"], MessageType.Success);
_jobs = await JobService.GetJobsAsync();
StateHasChanged();
}
}
catch (Exception ex)
{

View File

@ -21,9 +21,7 @@ else
@if (_allowexternallogin)
{
<button type="button" class="btn btn-primary" @onclick="ExternalLogin">@Localizer["Use"] @PageState.Site.Settings["ExternalLogin:ProviderName"]</button>
<br />
<br />
<br /><br />
}
@if (_allowsitelogin)
{
@ -49,15 +47,11 @@ else
</div>
<button type="button" class="btn btn-primary" @onclick="Login">@SharedLocalizer["Login"]</button>
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
<br />
<br />
<br /><br />
<button type="button" class="btn btn-secondary" @onclick="Forgot">@Localizer["ForgotPassword"]</button>
@if (PageState.Site.AllowRegistration)
{
<br />
<br />
<br /><br />
<NavLink href="@NavigateUrl("register")">@Localizer["Register"]</NavLink>
}
}

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