Compare commits

..

284 Commits

Author SHA1 Message Date
6bcb769fe5 Merge pull request #675 from sbwalker/master
prepare for 1.0.3 release
2020-08-07 10:40:00 -07:00
90110a653c prepare for 1.0.3 release 2020-08-07 13:39:19 -04:00
3c561cc413 Merge pull request #48 from oqtane/master
sync
2020-08-07 10:25:15 -07:00
73f9622ba2 Merge pull request #674 from sbwalker/master
prepare for 1.0.3 release
2020-08-07 10:24:31 -07:00
cf198ff781 prepare for 1.0.3 release 2020-08-07 13:23:58 -04:00
648fc56495 Merge pull request #673 from sbwalker/master
fixed very large file upload
2020-08-07 08:46:57 -07:00
ea6dc6b983 fixed very large file upload 2020-08-07 11:46:11 -04:00
c0e8d09ce1 Merge pull request #672 from sbwalker/master
allow user to reinstall current version
2020-08-06 13:48:07 -07:00
a471784cf3 allow user to reinstall current version 2020-08-06 16:46:22 -04:00
1d2a4bf484 Merge pull request #671 from sbwalker/master
include logging during module and theme installation
2020-08-06 13:37:55 -07:00
3fa620f3bc include logging during module and theme installation 2020-08-06 16:37:27 -04:00
35f186b532 Merge pull request #670 from sbwalker/master
fix regression bug caused by #649 related to installing nupkg packages
2020-08-06 13:10:51 -07:00
5cf35fd70a fix regression bug caused by #649 related to installing nupkg packages 2020-08-06 16:10:19 -04:00
1eef08eaeb Merge pull request #669 from sbwalker/master
modifications for System Update
2020-08-06 10:30:40 -07:00
1750f28a9f modifications for System Update 2020-08-06 13:30:06 -04:00
41edbc5e22 Merge pull request #668 from sbwalker/master
modifications for System Update feature
2020-08-04 10:07:35 -07:00
04257f75e7 modifications for System Update feature 2020-08-04 13:06:54 -04:00
5fb602f733 Merge pull request #667 from sbwalker/master
Improvements to System Update
2020-08-04 05:48:19 -07:00
94f0bdcce9 Improvements to System Update 2020-08-04 08:47:39 -04:00
0ba24f9a3a Update README.md 2020-08-04 08:42:28 -04:00
aac7d6b97a Merge pull request #664 from sbwalker/master
create 1.0.2 packages
2020-07-23 15:32:17 -04:00
bcc33af52b create 1.0.2 packages 2020-07-23 15:31:28 -04:00
24fd42636a Merge pull request #663 from sbwalker/master
preparing for 1.0.2 release
2020-07-23 15:08:27 -04:00
8d539d058c preparing for 1.0.2 release 2020-07-23 15:07:18 -04:00
abda377f6f Merge pull request #662 from sbwalker/master
fix regression bug caused by #648  - the entries within a nupkg (zip) package use the '/' separator - the fix in #648 was causing a wwwroot\wwwroot\... folder to be created on Windows
2020-07-23 14:59:25 -04:00
51bf822392 fix regression bug caused by #648 - the entries within a nupkg (zip) package use the '/' separator - the fix in #648 was causing a wwwroot\wwwroot\... folder to be created on Windows 2020-07-23 14:58:33 -04:00
d3f135a9c7 Merge pull request #47 from oqtane/master
sync
2020-07-23 14:41:29 -04:00
07ba99cc41 Merge pull request #661 from sbwalker/master
increase wait time for browser redirects during app restarts
2020-07-23 14:40:46 -04:00
336550c571 increase wait time for browser redirects during app restarts 2020-07-23 14:39:53 -04:00
679cc04178 Merge pull request #660 from sbwalker/master
optimize NotificationJob so that it only processes the sites for each tenant once.
2020-07-23 14:15:24 -04:00
75fe4e7c89 optimize NotificationJob so that it only processes the sites for each tenant once. 2020-07-23 14:14:29 -04:00
410f8c74e5 Merge pull request #649 from JoergH66/feature/LoadDependDlls_InstallManager
Fixes 2 external module installation problems
2020-07-23 11:39:35 -04:00
05f67d6a2a Merge pull request #659 from sbwalker/master
fixed scheduler so that it does not set NextExecution until after the job has finished executing
2020-07-23 11:39:19 -04:00
3a6cde0e24 fixed scheduler so that it does not set NextExecution until after the job has finished executing 2020-07-23 11:38:20 -04:00
fe1de2b243 Merge pull request #658 from sbwalker/master
Allow scheduled jobs to set next execution date, fix issue in site settings where logo field was not being populated, fixed compositing issue where deleted modules were being rendered.
2020-07-22 16:10:23 -04:00
62a6b5f28a Allow scheduled jobs to set next execution date, fix issue in site settings where logo field was not being populated, fixed compositing issue where deleted modules were being rendered. 2020-07-22 16:09:39 -04:00
d648fa0f02 Merge pull request #657 from ADefWebserver/master
Make a Deploy to Azure Button #168
2020-07-21 11:07:36 -04:00
e706e8cf1f Update README.md
Points the button to the Oqtane repository
2020-07-18 09:55:27 -07:00
9eb8a7e65c Update azuredeploy.json 2020-07-18 09:40:40 -07:00
11c610edf0 Update .deployment 2020-07-18 09:18:02 -07:00
a65cdbd7ad Rename azure.deployment to .deployment 2020-07-18 08:12:18 -07:00
97c56ba142 Create azure.deployment 2020-07-18 08:02:03 -07:00
9fe72a1c98 Update azuredeploy.json 2020-07-17 18:45:57 -07:00
7b40725534 Update README.md 2020-07-17 18:15:08 -07:00
5e1671afe3 Create azuredeploy.json 2020-07-17 18:10:22 -07:00
50d74cbcee Merge pull request #656 from sbwalker/master
modifications to ActionLink and ActionDialog controls, added logic to prevent IHostedService from blocking startup, made controller route prefix consistent in module template
2020-07-17 15:11:32 -04:00
bc73e5e3d0 modifications to ActionLink and ActionDialog controls, added logic to prevent IHostedService from blocking startup, made controller route prefix consistent in module template 2020-07-17 15:09:05 -04:00
b02bdee8cb Merge pull request #46 from oqtane/master
sync
2020-07-17 10:10:23 -04:00
9db4985b14 Merge pull request #655 from alexhendel/fix-path-handling
Fix directory separator for path operations
2020-07-16 10:27:34 -04:00
6f281c256b Merge pull request #654 from mikecasas/upstream-local
Should be moduleid as the entity id is added in the CreateAuthPolicyU…
2020-07-16 10:27:19 -04:00
807252c9e5 Fix directory separator for path operations 2020-07-15 16:09:19 +02:00
23e7f66188 Delete module id as it is getting added in CreateAuthPolicyUrl method. 2020-07-14 11:47:59 -04:00
57c500f4bc Merge remote-tracking branch 'github-mike/upstream-local' into upstream-local 2020-07-14 11:42:55 -04:00
fe302aa9e4 Should be moduleid as the entity id is added in the CreateAuthPolicyUrl method. 2020-07-12 18:59:20 -04:00
f14f927df7 Update README.md 2020-07-09 10:19:20 -04:00
c3f74a5217 Update README.md 2020-07-09 10:18:06 -04:00
457d1bb563 Update README.md 2020-07-09 10:17:34 -04:00
25918056cb Update README.md 2020-07-09 10:14:53 -04:00
86517dd793 Update README.md 2020-07-09 10:13:07 -04:00
00ce083a2c Update README.md 2020-07-09 10:11:28 -04:00
bce262cd8e Merge pull request #652 from sbwalker/master
remove line feeds from content during import
2020-07-09 08:46:50 -04:00
b5db62ef6a remove line feeds from content during import 2020-07-09 08:45:23 -04:00
3703d87d50 Merge pull request #651 from sbwalker/master
Html encode job log messages, add new IModule property to allow modules to specify Runtime support, provide feedback during module content import, remove default EditMode option at the Page level (should be implemented at Module level) - resolves issue where Admin modules could not be deleted, include link to Event Log in AddModuleMessage for Error message type, fixed fallback support for themes in siterouter, integrated auth policy into site templates for Module Creator
2020-07-08 19:57:13 -04:00
f515def414 Html encode job log messages, add new IModule property to allow modules to specify Runtime support, provide feedback during module content import, remove default EditMode option at the Page level (should be implemented at Module level) - resolves issue where Admin modules could not be deleted, include link to Event Log in AddModuleMessage for Error message type, fixed fallback support for themes in siterouter, integrated auth policy into site templates for Module Creator 2020-07-08 19:56:02 -04:00
4bdf20822f check whether the file is in use, dependent runtime-dlls will distribute 2020-07-08 14:06:41 +02:00
49f4e64cb4 Merge pull request #45 from oqtane/master
sync
2020-07-07 08:44:51 -04:00
e615263706 Merge pull request #647 from chlupac/Notifications
Notification changes UNDO
2020-07-07 08:43:52 -04:00
2a7e256116 Merge pull request #648 from alexhendel/fix-linux-theme-installation
Fix directory separator in InstallationManager
2020-07-07 08:43:37 -04:00
a083405b48 Fix directory seperator in InstallationManager 2020-07-07 09:56:24 +02:00
3ea280c82a Motification changes UNDO 2020-07-06 15:27:35 +02:00
192433f02d Merge pull request #44 from oqtane/master
sync
2020-07-05 11:32:20 -04:00
56a2e9dcea Merge pull request #644 from chlupac/ActionIcons
Icons in module actions menu
2020-07-03 15:16:47 -04:00
921cced1c8 Merge pull request #643 from chlupac/Notifications
Notification job optimalization
2020-07-03 15:16:14 -04:00
b17f679f38 Notification job optimalization 2020-07-03 10:19:12 +02:00
8e43fcab21 Icons in module actions menu 2020-07-03 10:15:45 +02:00
73c5092e46 Merge pull request #43 from oqtane/master
sync
2020-07-02 08:56:55 -04:00
56537e4785 Merge pull request #642 from svreic/bugfix/page-path-validation
Page path validation
2020-07-02 08:13:39 -04:00
3bd7d7196d Merge pull request #640 from PoisnFang/routing
Url parameter helper enhancements
2020-07-02 08:11:14 -04:00
7b5a192b82 Added double page path validation 2020-07-02 09:47:42 +02:00
d4be058d07 Can get parameters without template
clear urlParameters dictionary if template fails. Removed UrlParametersTemplate property and UrlParamerters auto dictionary
2020-07-01 15:15:39 -07:00
6c20fea46a Merge pull request #641 from chlupac/NotifyRepo
Notification Repository Breaking change fix
2020-07-01 14:22:26 -04:00
2e7cfefb2e Notification Repository Breaking change fix 2020-07-01 14:23:55 +02:00
038894cf64 Enhancement to url parameters helper in modulebase 2020-07-01 01:35:06 -07:00
954e30d89f Save url parameter action segments 2020-06-30 16:01:16 -07:00
93d9c4534d Merge pull request #42 from oqtane/master
sync
2020-06-30 16:56:08 -04:00
468ca8c6a9 Merge pull request #639 from PoisnFang/routing
Hot fix for homepage routing
2020-06-30 16:54:54 -04:00
e7a4c08dea Now also properly routes in admin modules 2020-06-30 13:51:48 -07:00
69d639ee42 Hot fix for homepage routing 2020-06-30 13:42:35 -07:00
a780569a6f Merge pull request #41 from oqtane/master
sync
2020-06-30 16:16:11 -04:00
568c283efd Merge pull request #638 from PoisnFang/routing
Module Router Enhancement
2020-06-30 16:15:27 -04:00
fccdd07a08 Replaced token identifiers for { } 2020-06-30 12:59:19 -07:00
5e816ea912 Removed anchor property and hash is only set if there is anchor 2020-06-30 12:49:56 -07:00
cb2d529689 added in GetUrlParameters route to Module Index action 2020-06-30 04:16:08 -07:00
c5037e7084 Url parameters working on any page, plus queries and anchors 2020-06-30 03:41:35 -07:00
fdc39d57fb Module Router Enhancement
Allows for PageVariables through the URL
2020-06-27 11:49:24 -07:00
4960e2c668 Update README.md 2020-06-26 08:45:13 -04:00
66cc3a1392 Merge pull request #637 from sbwalker/master
improvements for custom authorization policy usage
2020-06-25 10:24:54 -04:00
6e7c8e7b05 improvements for custom authorization policy usage 2020-06-25 10:23:27 -04:00
727b943fa3 Merge pull request #636 from sbwalker/master
added ModuleControlBase
2020-06-25 09:32:56 -04:00
a4a0334ec0 added ModuleControlBase 2020-06-25 09:31:21 -04:00
9ea5da525b Merge pull request #634 from sbwalker/master
preparing for 1.0.1 release
2020-06-23 09:42:25 -04:00
fa15a5e44b preparing for 1.0.1 release 2020-06-23 09:41:17 -04:00
35d037bbf4 Merge pull request #40 from oqtane/master
sync
2020-06-23 09:13:58 -04:00
e8387103f1 Merge pull request #631 from svreic/bugfix/fixing-theme-service
Fixed ThemeService using tenant aware api calls
2020-06-22 16:59:54 -04:00
16965fd027 Merge pull request #632 from sbwalker/master
enhancement to load dependencies from the /bin if they are not loaded automatically
2020-06-22 16:59:43 -04:00
f81ef89c61 enhancement to load dependencies from the /bin if they are not loaded automatically 2020-06-22 16:58:41 -04:00
6034a161e7 Fixed ThemeService using tenant aware api calls 2020-06-22 08:41:09 +02:00
ff23865711 Merge pull request #39 from oqtane/master
sync
2020-06-19 17:30:38 -04:00
3ac4116a84 Merge pull request #628 from thabaum/dashboard-table-col-size
Fix Dashboard button/input table col size
2020-06-19 17:29:27 -04:00
65df0549fd Merge pull request #625 from mikecasas/feature-email
Send notification based on a future datetime
2020-06-19 17:24:31 -04:00
04ffb079ea Merge pull request #630 from sbwalker/master
upgrade themes to latest Bootstrap, fix breaking change to ThemeBase due to IThemeControl being removed
2020-06-19 17:23:48 -04:00
99d99ca6ad upgrade themes to latest Bootstrap, fix breaking change to ThemeBase due to IThemeControl being removed 2020-06-19 17:22:26 -04:00
0659fc53b7 default launch settings 2020-06-18 23:16:18 -07:00
2febd81b4a default connection 2020-06-18 23:14:21 -07:00
0f814bfefe Update launchSettings.json 2020-06-18 23:06:33 -07:00
120066fce1 default 2020-06-18 23:01:59 -07:00
f4ca525bb5 Delete appsettings.json 2020-06-18 22:50:14 -07:00
5dced08f7a removes password autofill autocomplete="new-password" 2020-06-18 22:47:41 -07:00
882412b8ff button/input columns style="width: 1px" 2020-06-18 22:22:16 -07:00
563345638a Merge remote-tracking branch 'oqtane.framework-upstream/master' into dashboard-table-col-size 2020-06-18 18:14:15 -07:00
20b83c8809 Updated csproj. 2020-06-18 13:09:57 -04:00
7d89670930 Fixed the sql script file name and added update for null values. 2020-06-18 12:58:30 -04:00
40571bfb6e Bug Fix.
Not sure why the email address was not being added to the notification.
2020-06-18 08:40:43 -04:00
5a7a47ef27 Merge pull request #38 from oqtane/master
sync
2020-06-18 08:20:56 -04:00
ee5553ad8a Step 2. 2020-06-18 07:35:40 -04:00
ab8a1e7324 Step 1. 2020-06-18 06:13:15 -04:00
9e633d2d6d Merge pull request #624 from jimspillane/RemoveAsyncFromLoadJS
Remove async from scripts
2020-06-17 14:20:59 -04:00
975e7217a5 Remove async from scripts
Run SPA scripts synchronously to ensure load order
2020-06-17 12:25:36 -04:00
95053d8671 Merge pull request #18 from oqtane/master
Sync Master
2020-06-17 12:14:54 -04:00
61498862b5 Merge pull request #623 from sbwalker/master
add styling to install wizard
2020-06-17 11:29:25 -04:00
a57e25d3d3 add styling to install wizard 2020-06-17 11:27:44 -04:00
7cd18b1fe1 Merge pull request #622 from sbwalker/master
refactored script resource declaration to allow for bundling, made script loading async, refactored RichTextEditor to use new method
2020-06-17 10:28:26 -04:00
0636227284 refactored script resource declaration to allow for bundling, made script loading async, refactored RichTextEditor to use new method 2020-06-17 10:27:14 -04:00
d4d12b6f41 Merge pull request #621 from sbwalker/master
resolve #566 by moving Bootstrap declaration into theme
2020-06-16 17:39:15 -04:00
7c24bae753 resolve #566 by moving Bootstrap declaration into theme 2020-06-16 17:38:06 -04:00
06480d9a7e Merge pull request #619 from sbwalker/master
fix #618
2020-06-15 15:48:52 -04:00
71b3b695fc fix #618 2020-06-15 15:47:35 -04:00
db6d5d2b64 Merge pull request #616 from sbwalker/master
quick fix for #475 - cannot save role
2020-06-15 09:24:09 -04:00
7d7e8e9e66 quick fix for #475 - cannot save role 2020-06-15 09:22:35 -04:00
354382a580 Merge pull request #615 from sbwalker/master
minor cleanup
2020-06-14 22:10:25 -04:00
288cad3d3f minor cleanup 2020-06-14 22:08:52 -04:00
597a935cac Merge pull request #614 from sbwalker/master
added general exception UI handler for #605
2020-06-14 21:48:39 -04:00
1e4b2404c4 added general exception UI handler for #605 2020-06-14 21:47:11 -04:00
048d8f1b87 Merge pull request #613 from sbwalker/master
fix #595 - remove event log entry under scenario where a user is unauthenticated
2020-06-14 21:31:01 -04:00
ec416a7fef fix #595 - remove event log entry under scenario where a user is unauthenticated 2020-06-14 21:29:14 -04:00
c8630d7b05 Merge pull request #612 from sbwalker/master
fix #600 Unhandled exception opening a user's profile
2020-06-14 21:14:46 -04:00
42696eacbd fix #600 Unhandled exception opening a user's profile 2020-06-14 21:12:55 -04:00
b668a5d0ae Merge pull request #611 from sbwalker/master
migrate script naming convention
2020-06-14 13:07:05 -04:00
4285603563 migrate script naming convention 2020-06-14 13:06:33 -04:00
1c0d24ac25 Merge pull request #610 from sbwalker/master
refactoring of #518 to simplify registration of scripts in modules and themes
2020-06-14 12:08:05 -04:00
4e6b4a20ef refactoring of #518 to simplify registration of scripts in modules and themes 2020-06-14 12:07:16 -04:00
ea89cc1a64 Merge pull request #37 from oqtane/master
sync
2020-06-14 11:05:20 -04:00
79622819aa Merge pull request #609 from sbwalker/master
sync
2020-06-14 11:01:08 -04:00
88e3a9adc3 Merge pull request #608 from jimspillane/AddJavaScriptDependencyManager
Add JavaScript dependency manager
2020-06-14 11:00:27 -04:00
f0c95c46c9 Merge pull request #36 from oqtane/master
sync
2020-06-12 13:22:07 -04:00
1a0e8f7e19 Merge pull request #607 from chlupac/SiteRouterFix
small fixes
2020-06-12 13:04:44 -04:00
b5d4f8b176 Merge pull request #606 from chlupac/swagger
Add possibility to switch off swagger on production.
2020-06-12 13:02:58 -04:00
8053cc0af6 small fixes 2020-06-12 17:48:50 +02:00
134f21e29c Merge pull request #35 from oqtane/master
sync
2020-06-12 09:41:19 -04:00
a67330dbbb Merge pull request #586 from thabaum/patch-13
To/From Fields enabled/disabled, reply message fix
2020-06-12 09:40:16 -04:00
06820b9b63 Merge pull request #585 from thabaum/patch-12
'...' added, Reply Removed
2020-06-12 09:37:57 -04:00
f5d47a0308 Merge pull request #599 from BhanuKorthiwada/patch-1
Update README.md
2020-06-12 09:34:44 -04:00
937ae32a9e Add JS dependency manager
Added LoadJS(https://github.com/muicss/loadjs) and migrated Quill interop to use LoadJS.
2020-06-12 08:07:23 -04:00
5124dea72c Add JS dependency manager
Added LoadJS(https://github.com/muicss/loadjs) and migrated Quill interop to use LoadJS.
2020-06-10 21:09:10 -04:00
2ceeb25d0e Merge pull request #17 from oqtane/master
Sync upstream master
2020-06-10 12:20:54 -04:00
e2732d83bd Merge pull request #34 from oqtane/master
sync
2020-06-09 15:19:18 -04:00
3e56e9cc04 Update README.md 2020-06-09 09:57:09 +05:30
836c4505b9 fix bug #589 - Unhandled exception when trying to edit MyPage 2020-06-08 13:48:41 -04:00
ef5ae77060 Merge pull request #596 from svreic/FixingGetInstances
Fixed type filtering in assembly extensions
2020-06-08 13:47:19 -04:00
86b7fe193f Merge pull request #597 from sbwalker/master
fix bug #589 - Unhandled exception when trying to edit MyPage
2020-06-08 13:47:09 -04:00
3503f20255 Fixed type filtering in assembly extensions 2020-06-08 11:36:32 +02:00
51b69e05fb Original Message text area full bottom screen 2020-06-06 15:28:48 -07:00
095a95a3a9 removes message carriage returns 2020-06-06 11:46:35 -07:00
8d17153686 replace dashes with underscores 2020-06-06 08:03:42 -07:00
6efbeeb2b4 Change dashes to underscores 2020-06-06 08:02:08 -07:00
af1aab5b8d Updated to latest framework message username
addresses conflicts
2020-06-05 17:57:25 -07:00
f854b948ce namespaces removed last commit 2020-06-04 21:22:48 -07:00
7b9cd39cb7 To/From Fields enabled/disabled, reply message fix 2020-06-04 21:20:48 -07:00
6f6f2b77d7 '...' added, Reply Removed 2020-06-04 21:11:24 -07:00
ed2822ac7c fixed external module template to specify dependencies 2020-06-04 12:53:14 -04:00
41348b285d Merge pull request #583 from sbwalker/master
fixed external module template to specify dependencies
2020-06-04 12:51:40 -04:00
5b0da056b4 Restore support for third party assembly dependencies in modules and themes when running om Wasm 2020-06-04 12:48:29 -04:00
2d079bdc06 Merge pull request #582 from sbwalker/master
Restore support for third party assembly dependencies in modules and themes when running om Wasm
2020-06-04 12:47:14 -04:00
aed71fbf96 Add possibility to switch off swagger on production. 2020-06-04 07:10:45 +02:00
4ebdf5721c Merge pull request #33 from oqtane/master
sync
2020-06-03 19:47:04 -04:00
99cad13890 restrict user data leakage 2020-06-03 19:46:47 -04:00
4edb3f32f0 Merge pull request #580 from chlupac/UserManagerPersistence
User manager search persistence
2020-06-03 19:46:36 -04:00
01a3b7ed95 Merge pull request #581 from sbwalker/master
restrict user data leakage
2020-06-03 19:46:02 -04:00
c79199357e Merge pull request #579 from chlupac/StartupFix
Move module startup install up to allow install middleware
2020-06-03 19:45:47 -04:00
e24c6fc235 Move module startup install up to allow install middleware 2020-06-03 20:13:48 +02:00
cdc4de432a User manager search persistence 2020-06-03 20:10:52 +02:00
5544d2bed3 fix dynamic creation of remote script tags 2020-06-02 16:10:02 -04:00
aa4b00c900 Merge pull request #578 from sbwalker/master
fix dynamic creation of remote script tags
2020-06-02 16:08:34 -04:00
c4f1d37421 improve user experience after app restarts 2020-06-02 14:21:57 -04:00
c6b22d660b Merge pull request #577 from sbwalker/master
improve user experience after app restarts
2020-06-02 14:20:28 -04:00
ec11587b28 Merge pull request #32 from oqtane/master
sync
2020-06-01 14:58:41 -04:00
1b7ca45d4a Added support for friendly names and thumbnails in theme, layout, and container components. Added fallback support during loading for themes, layout, and containers. 2020-06-01 14:58:46 -04:00
01491b063d Merge pull request #574 from chlupac/OqtaneIgnore
OqtaneIgnore implementation fix
2020-06-01 14:58:04 -04:00
4142cc63e7 Merge pull request #576 from sbwalker/master
Added support for friendly names and thumbnails in theme, layout, and container components. Added fallback support during loading for themes, layout, and containers.
2020-06-01 14:57:21 -04:00
20dc749d05 Merge pull request #575 from chlupac/ControlPanel
Control panel Pane persistence & default selection fix.
2020-06-01 08:09:07 -04:00
4144be5323 Control panel Pane persistence & default selection fix. 2020-06-01 10:12:33 +02:00
27a3ac8d1c OqtaneIgnore implementation fix 2020-06-01 09:21:53 +02:00
f45cb8b069 fix theme selection in sites 2020-05-31 22:56:19 -04:00
0cc325ee95 Merge pull request #573 from sbwalker/master
fix theme selection in sites
2020-05-31 22:54:54 -04:00
7d21cfefc1 restrict container selection to the current theme, hide layout selection if theme does not support layouts, make behavior consistent for all theme/layout/container selection 2020-05-31 22:53:11 -04:00
652d46f64a Merge pull request #572 from sbwalker/master
restrict container selection to the current theme, hide layout selection if theme does not support layouts, make behavior consistent for all theme/layout/container selection
2020-05-31 22:52:05 -04:00
be4813d9c0 Merge pull request #31 from oqtane/master
sync
2020-05-31 20:45:12 -04:00
fe92d3d74f Merge pull request #538 from thabaum/patch-9
Site Settings: removes ONLY " <Select Container>"Theme and Layout drop down options
2020-05-31 20:42:25 -04:00
833ea9461a Merge pull request #568 from chlupac/ActionLink
Allow to show icon only in action link
2020-05-31 09:54:04 -04:00
47a917a3df Merge pull request #16 from oqtane/master
Sync master
2020-05-30 18:41:08 -04:00
cb484665ca Allow to show icon only in action link 2020-05-30 21:24:40 +02:00
64b0c2f128 Merge pull request #564 from sbwalker/master
addressed consistency between theme loading and moduledefinition loading, added theme detailed UI view
2020-05-29 16:27:04 -04:00
6402723d2a addressed consistency between theme loading and moduledefinition loading, added theme detailed UI view 2020-05-29 16:27:02 -04:00
b1a007491f Merge pull request #30 from oqtane/master
sync
2020-05-29 10:53:46 -04:00
17ef268594 Merge pull request #559 from chlupac/ThemeRepository
OqtaneIgnore implementation to theme elements
2020-05-29 10:42:33 -04:00
58d97dd731 OqtaneIgnore implementation to theme elements 2020-05-29 16:09:27 +02:00
ee3a4d1624 Merge pull request #556 from iJungleboy/patches/fix544
Patches/fix544
2020-05-29 08:38:22 -04:00
f9035f8fdf Update README.md 2020-05-28 17:32:06 -04:00
3a5b6954e3 fix for #525 when running locally without a network connection 2020-05-28 16:24:22 -04:00
1794d54a3f Merge pull request #557 from sbwalker/master
fix for #525 when running locally without a network connection
2020-05-28 16:23:15 -04:00
7d251b20cc Fix collection of theme information because of improper namespace checks
https://github.com/oqtane/oqtane.framework/issues/554
2020-05-28 21:07:30 +02:00
0f09df13b5 page redirect support and added missing unique indexes on database tables 2020-05-28 14:48:00 -04:00
73763f1623 Merge pull request #555 from sbwalker/master
page redirect support and added missing unique indexes on database tables
2020-05-28 14:46:36 -04:00
1b2c7772ef Merge pull request #29 from oqtane/master
sync
2020-05-28 14:39:10 -04:00
3b2583a1bd Merge pull request #553 from chlupac/HorizontalMenuFix
Horizontal menu fix
2020-05-28 14:37:24 -04:00
f7470e3c5b Horizontal menu fix 2020-05-28 20:00:28 +02:00
19f8b3d429 Merge pull request #28 from oqtane/master
sync
2020-05-28 10:30:12 -04:00
fd249a7734 Merge pull request #551 from chlupac/TryGetQueryValue
Uri extensions for read query values
2020-05-28 09:51:01 -04:00
d5f3b7513d Merge pull request #549 from chlupac/UserSearch
Search in User management
2020-05-28 09:45:45 -04:00
2a3b7caa9f Merge pull request #548 from jimspillane/FixWildcardRoute
Fix Alias name route
2020-05-28 09:45:34 -04:00
612a820dac fixed stylesheet and script removal logic 2020-05-28 09:44:41 -04:00
e80f42f1d9 Merge pull request #552 from sbwalker/master
fixed stylesheet and script removal logic
2020-05-28 09:43:23 -04:00
8b5004c628 Merge pull request #1 from oqtane/master
sync
2020-05-28 12:47:23 +02:00
151e37c470 Uri extensions for read query values
- safe way to check if key is presented
- safe parsing int values in query
- should replace int.Parse()

  _jobId = Int32.Parse(PageState.QueryString["id"]);
  can throw unhandled exception when "id" is not int

  correct way
  if (PageState.Uri.TryGetQueryValueInt("id",out _jobId)) ....
2020-05-28 12:26:23 +02:00
e935451d93 Search in User management 2020-05-28 11:33:47 +02:00
b0af00aa47 Fix Alias route
Added wildcard route catchall.
2020-05-27 16:58:50 -04:00
cbbac26881 Merge pull request #15 from oqtane/master
Sync upstream
2020-05-27 16:55:10 -04:00
f2230dd530 Merge pull request #544 from chlupac/nuget
Generate nugets in correct format
2020-05-27 16:12:51 -04:00
963148c639 Refactor Javascript and Stylesheet loading 2020-05-27 16:03:38 -04:00
215e52e42e Merge pull request #547 from sbwalker/master
Refactor Javascript and Stylesheet loading
2020-05-27 16:02:20 -04:00
945d7870c0 Merge pull request #14 from oqtane/master
Sync master
2020-05-26 18:46:49 -04:00
cc40733cff Merge pull request #27 from oqtane/master
sync
2020-05-26 10:39:21 -04:00
795f591da2 Generate nugets in correct format 2020-05-26 09:28:37 +02:00
4cab49e022 Merge pull request #535 from thabaum/patch-8
Oqtane Theme:  CSS style module actions dropdown text #fff
2020-05-25 11:33:07 -04:00
3b685fe571 Merge pull request #541 from jimspillane/ChangeJavaScriptNamespaceToOqtane
Change JavaScript namespace from interop to Oqtane
2020-05-25 11:32:54 -04:00
97df673609 Change JavaScript namespace from interop to Oqtane 2020-05-24 23:04:55 -04:00
cbcec0481a removed "Select Container, Theme, Layout" options 2020-05-24 19:12:47 -07:00
2f272ef4b6 style module actions dropdown text #fff 2020-05-24 17:47:27 -07:00
a4ed06f2fc Merge pull request #13 from oqtane/master
Sync upstream
2020-05-24 19:43:11 -04:00
3339690e2a Improvements to ModuleCreator external template to use Package references and include framework in Nuspec file 2020-05-24 19:11:35 -04:00
e4b37c17d8 Merge pull request #534 from sbwalker/master
Improvements to ModuleCreator external template to use Package references and include framework in Nuspec file
2020-05-24 19:10:21 -04:00
f1f07f45c6 Merge pull request #531 from chlupac/ControlPanel
Control panel persistence between module addings
2020-05-24 10:54:10 -04:00
068803615b Notification job fill mail subject 2020-05-22 21:29:52 +02:00
ddbb08ea75 Control panel state persistence 2020-05-22 21:29:06 +02:00
6ac2b64d7d improvements to module creator templates 2020-05-22 13:39:59 -04:00
93497eb776 Merge pull request #530 from sbwalker/master
improvements to module creator templates
2020-05-22 13:39:04 -04:00
e1b0dbcdf7 modification to JSInterop and Quill 2020-05-22 11:51:57 -04:00
c2ddd248e4 Merge pull request #529 from sbwalker/master
modification to JSInterop and Quill
2020-05-22 11:51:04 -04:00
a4ec456205 Merge branch 'master' of https://github.com/thabaum/Oqtane.Framework 2020-05-21 21:10:57 -07:00
23764d1ae4 Merge pull request #3 from oqtane/master
update
2020-05-21 21:07:22 -07:00
f77a8a964f Merge remote-tracking branch 'oqtane.framework-upstream/master' 2020-05-21 15:52:54 -07:00
c089b90659 modifications to JSInterop in RichTextEditor 2020-05-21 15:55:58 -04:00
32cd34b090 Merge pull request #522 from sbwalker/master
modifications to JSInterop in RichTextEditor
2020-05-21 15:54:53 -04:00
d8fca5de20 module creator templates need to be in the server project in order to be distributed with application 2020-05-20 15:31:09 -04:00
2f8a15fb89 Merge pull request #519 from sbwalker/master
module creator templates need to be in the server project in order to be distributed with application
2020-05-20 15:30:44 -04:00
1495a5c017 fixed references in external template 2020-05-20 14:59:57 -04:00
61b1b710db Merge pull request #517 from sbwalker/master
fixed references in external template
2020-05-20 14:58:56 -04:00
24579dc4d0 Merge pull request #12 from oqtane/master
Sync master
2020-05-20 13:04:32 -04:00
066ef44773 fixes to upgrade project 2020-05-20 11:57:41 -04:00
1355233b92 Merge pull request #515 from sbwalker/master
fixes to upgrade project
2020-05-20 11:56:32 -04:00
dc55c5b3ec Merge pull request #26 from oqtane/master
sync
2020-05-20 09:19:36 -04:00
ed3f07ff61 Merge pull request #514 from mikecasas/external-module-creator-refs
External module creator refs
2020-05-20 08:52:16 -04:00
028485b653 Merge remote-tracking branch 'upstream/master' into external-module-creator-refs 2020-05-20 08:06:18 -04:00
b43d191536 Merge pull request #25 from oqtane/master
sync
2020-05-20 08:03:39 -04:00
43dfad38c9 Merge pull request #513 from mikecasas/fix-01
Update reference.
2020-05-20 08:02:30 -04:00
6f1e930474 Updated the versions. 2020-05-20 06:51:22 -04:00
1d52de53a6 Update reference. 2020-05-20 06:47:57 -04:00
3aec68d9fa added border 2020-05-19 17:59:57 -04:00
4dc24f4c78 Merge pull request #511 from sbwalker/master
added border
2020-05-19 17:58:53 -04:00
a73e088abc Update README.md 2020-05-19 17:49:43 -04:00
b61446a50a updated screenshots 2020-05-19 17:49:11 -04:00
31773abb01 Merge pull request #510 from sbwalker/master
updated screenshots
2020-05-19 17:48:05 -04:00
85c491224d Update README.md 2020-05-19 14:55:52 -04:00
71f79bd90b Merge pull request #1 from oqtane/master
update master branch
2020-05-06 17:55:24 -07:00
211 changed files with 3289 additions and 1690 deletions

2
.deployment Normal file
View File

@ -0,0 +1,2 @@
[config]
project = Oqtane.Server/Oqtane.Server.csproj

View File

@ -28,7 +28,7 @@
</table>
<Pager Items="@_files">
<Header>
<th>&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>Name</th>
<th>Modified</th>
<th>Type</th>

View File

@ -63,10 +63,18 @@
</tr>
<tr>
<td>
<Label For="retention-log" HelpText="What items do you want in the retention log">Retention Log (Items): </Label>
<Label For="retention" HelpText="Number of log entries to retain for this job">Retention Log (Items): </Label>
</td>
<td>
<input id="retention-log" class="form-control" @bind="@_retentionHistory" />
<input id="retention" class="form-control" @bind="@_retentionHistory" />
</td>
</tr>
<tr>
<td>
<Label For="next" HelpText="Next execution for this job.">Next Execution: </Label>
</td>
<td>
<input id="next" class="form-control" @bind="@_nextExecution" />
</td>
</tr>
</table>
@ -83,6 +91,7 @@
private string _startDate = string.Empty;
private string _endDate = string.Empty;
private string _retentionHistory = string.Empty;
private string _nextExecution = string.Empty;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
@ -102,6 +111,7 @@
_startDate = (job.StartDate != null) ? job.StartDate.ToString() : string.Empty;
_endDate = (job.EndDate != null) ? job.EndDate.ToString() : string.Empty;
_retentionHistory = job.RetentionHistory.ToString();
_nextExecution = job.NextExecution.ToString();
}
}
catch (Exception ex)
@ -140,6 +150,15 @@
job.EndDate = DateTime.Parse(_endDate);
}
if (_nextExecution == string.Empty)
{
job.NextExecution = null;
}
else
{
job.NextExecution = DateTime.Parse(_nextExecution);
}
job.RetentionHistory = int.Parse(_retentionHistory);
try

View File

@ -15,14 +15,14 @@ else
<Pager Items="@_jobs">
<Header>
<th>&nbsp;</th>
<th>&nbsp;</th>
<th>&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>Name</th>
<th>Status</th>
<th>Frequency</th>
<th>Next Execution</th>
<th>&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
</Header>
<Row>
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.JobId.ToString())" /></td>

View File

@ -22,7 +22,7 @@ else
<td>@context.FinishDate</td>
</Row>
<Detail>
<td colspan="4">@context.Notes</td>
<td colspan="4">@((MarkupString)context.Notes)</td>
</Detail>
</Pager>
}

View File

@ -1,7 +1,6 @@
@namespace Oqtane.Modules.Admin.Login
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IJSRuntime JsRuntime
@inject IUserService UserService
@inject IServiceProvider ServiceProvider
@ -96,7 +95,7 @@
{
await logger.LogInformation("Login Successful For Username {Username}", _username);
// complete the login on the server so that the cookies are set correctly on SignalR
var interop = new Interop(JsRuntime);
var interop = new Interop(JSRuntime);
string antiforgerytoken = await interop.GetElementByName("__RequestVerificationToken");
var fields = new { __RequestVerificationToken = antiforgerytoken, username = _username, password = _password, remember = _remember, returnurl = _returnUrl };
await interop.SubmitForm($"/{PageState.Alias.AliasId}/pages/login/", fields);

View File

@ -49,7 +49,7 @@ else
{
<Pager Items="@_logs">
<Header>
<th>&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>Date</th>
<th>Level</th>
<th>Feature</th>

View File

@ -1,49 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Oqtane.Modules;
using Oqtane.Services;
using Oqtane.Shared;
using [Owner].[Module]s.Models;
namespace [Owner].[Module]s.Services
{
public class [Module]Service : ServiceBase, I[Module]Service, IService
{
private readonly SiteState _siteState;
public [Module]Service(HttpClient http, SiteState siteState) : base(http)
{
_siteState = siteState;
}
private string Apiurl=> CreateApiUrl(_siteState.Alias, "[Module]");
public async Task<List<[Module]>> Get[Module]sAsync(int ModuleId)
{
List<[Module]> [Module]s = await GetJsonAsync<List<[Module]>>($"{Apiurl}?moduleid={ModuleId}");
return [Module]s.OrderBy(item => item.Name).ToList();
}
public async Task<[Module]> Get[Module]Async(int [Module]Id)
{
return await GetJsonAsync<[Module]>($"{Apiurl}/{[Module]Id}");
}
public async Task<[Module]> Add[Module]Async([Module] [Module])
{
return await PostJsonAsync<[Module]>($"{Apiurl}?entityid={[Module].ModuleId}", [Module]);
}
public async Task<[Module]> Update[Module]Async([Module] [Module])
{
return await PutJsonAsync<[Module]>($"{Apiurl}/{[Module].[Module]Id}?entityid={[Module].ModuleId}", [Module]);
}
public async Task Delete[Module]Async(int [Module]Id)
{
await DeleteAsync($"{Apiurl}/{[Module]Id}");
}
}
}

View File

@ -1 +0,0 @@
This is the location where static resources for third party libraries should be located ( the third party library assemblies will be included in the /lib folder ). They should be placed in subfolders which match the naming convention of the third party library. When the module package is deployed the static resource subfolders will be extracted under the web root.

View File

@ -1 +0,0 @@
This is the location where static resources such as images, style sheets, or scripts for this module will be located. Static assets can be organized in subfolders. When the module package is deployed the assets will be extracted under the web root in a folder that matches the module namespace.

View File

@ -10,23 +10,23 @@
<TabStrip>
@if (_packages.Count > 0)
{
<TabPanel Name="Download">
<ModuleMessage Type="MessageType.Info" Message="Download one or more modules from the list below. Once you are ready click Install to complete the installation."></ModuleMessage>
<Pager Items="@_packages">
<Header>
<th>Name</th>
<th>Version</th>
<th></th>
</Header>
<Row>
<td>@context.Name</td>
<td>@context.Version</td>
<td>
<button type="button" class="btn btn-primary" @onclick=@(async () => await DownloadModule(context.PackageId, context.Version))>Download</button>
</td>
</Row>
</Pager>
</TabPanel>
<TabPanel Name="Download">
<ModuleMessage Type="MessageType.Info" Message="Download one or more modules from the list below. Once you are ready click Install to complete the installation."></ModuleMessage>
<Pager Items="@_packages">
<Header>
<th>Name</th>
<th>Version</th>
<th style="width: 1px"></th>
</Header>
<Row>
<td>@context.Name</td>
<td>@context.Version</td>
<td>
<button type="button" class="btn btn-primary" @onclick=@(async () => await DownloadModule(context.PackageId, context.Version))>Download</button>
</td>
</Row>
</Pager>
</TabPanel>
}
<TabPanel Name="Upload">
<table class="table table-borderless">
@ -77,8 +77,10 @@
{
try
{
ShowProgressIndicator();
var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 10);
await ModuleDefinitionService.InstallModuleDefinitionsAsync();
NavigationManager.NavigateTo(NavigateUrl());
}
catch (Exception ex)
{

View File

@ -75,12 +75,20 @@
</tr>
<tr>
<td>
<Label For="license" HelpText="The license of the module">License: </Label>
<Label For="license" HelpText="The module license terms">License: </Label>
</td>
<td>
<textarea id="license" class="form-control" @bind="@_license" rows="5" disabled></textarea>
</td>
</tr>
<tr>
<td>
<Label For="runtimes" HelpText="The Blazor runtimes which this module supports">Runtimes: </Label>
</td>
<td>
<input id="runtimes" class="form-control" @bind="@_runtimes" disabled />
</td>
</tr>
</table>
</Section>
</TabPanel>
@ -110,6 +118,7 @@
private string _url = "";
private string _contact = "";
private string _license = "";
private string _runtimes = "";
private string _permissions;
private string _createdby;
private DateTime _createdon;
@ -139,6 +148,7 @@
_url = moduleDefinition.Url;
_contact = moduleDefinition.Contact;
_license = moduleDefinition.License;
_runtimes = moduleDefinition.Runtimes;
_permissions = moduleDefinition.Permissions;
_createdby = moduleDefinition.CreatedBy;
_createdon = moduleDefinition.CreatedOn;

View File

@ -14,27 +14,27 @@ else
<Pager Items="@_moduleDefinitions">
<Header>
<th>&nbsp;</th>
<th>&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>Name</th>
<th>Version</th>
<th>&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
</Header>
<Row>
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.ModuleDefinitionId.ToString())" /></td>
<td>
@if (context.AssemblyName != "Oqtane.Client")
{
{
<ActionDialog Header="Delete Module" Message="@("Are You Sure You Wish To Delete The " + context.Name + " Module?")" Action="Delete" Security="SecurityAccessLevel.Host" Class="btn btn-danger" OnClick="@(async () => await DeleteModule(context))" />
}
}
</td>
<td>@context.Name</td>
<td>@context.Version</td>
<td>
@if (UpgradeAvailable(context.ModuleDefinitionName, context.Version))
{
{
<button type="button" class="btn btn-success" @onclick=@(async () => await DownloadModule(context.ModuleDefinitionName, context.Version))>Upgrade</button>
}
}
</td>
</Row>
</Pager>
@ -55,20 +55,26 @@ else
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading Modules {Error}", ex.Message);
AddModuleMessage("Error Loading Modules", MessageType.Error);
if (_moduleDefinitions == null)
{
await logger.LogError(ex, "Error Loading Modules {Error}", ex.Message);
AddModuleMessage("Error Loading Modules", MessageType.Error);
}
}
}
private bool UpgradeAvailable(string moduledefinitionname, string version)
{
var upgradeavailable = false;
var package = _packages.Where(item => item.PackageId == Utilities.GetTypeName(moduledefinitionname)).FirstOrDefault();
if (package != null)
if (_packages != null)
{
upgradeavailable = (Version.Parse(package.Version).CompareTo(Version.Parse(version)) > 0);
}
var package = _packages.Where(item => item.PackageId == Utilities.GetTypeName(moduledefinitionname)).FirstOrDefault();
if (package != null)
{
upgradeavailable = (Version.Parse(package.Version).CompareTo(Version.Parse(version)) > 0);
}
}
return upgradeavailable;
}
@ -77,24 +83,27 @@ else
try
{
await PackageService.DownloadPackageAsync(moduledefinitionname, version, "Modules");
await ModuleDefinitionService.InstallModuleDefinitionsAsync();
await logger.LogInformation("Module Downloaded {ModuleDefinitionName} {Version}", moduledefinitionname, version);
NavigationManager.NavigateTo(NavigateUrl());
ShowProgressIndicator();
var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 10);
await ModuleDefinitionService.InstallModuleDefinitionsAsync();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Downloading Module {ModuleDefinitionName} {Version} {Error}", moduledefinitionname, version, ex.Message);
AddModuleMessage("Error Downloading Module", MessageType.Error);
}
}
}
private async Task DeleteModule(ModuleDefinition moduleDefinition)
{
try
{
ShowProgressIndicator();
var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 10);
await ModuleDefinitionService.DeleteModuleDefinitionAsync(moduleDefinition.ModuleDefinitionId, moduleDefinition.SiteId);
await logger.LogInformation("Module Deleted {ModuleDefinition}", moduleDefinition);
NavigationManager.NavigateTo(NavigateUrl());
}
catch (Exception ex)
{

View File

@ -31,9 +31,15 @@
{
try
{
await ModuleService.ImportModuleAsync(ModuleState.ModuleId, _content);
StateHasChanged();
NavigationManager.NavigateTo(NavigateUrl());
bool success = await ModuleService.ImportModuleAsync(ModuleState.ModuleId, _content);
if (success)
{
AddModuleMessage("Content Imported Successfully", MessageType.Success);
}
else
{
AddModuleMessage("A Problem Was Encountered Importing Content. Please Ensure The Content Is Formatted Correctly For The Module.", MessageType.Warning);
}
}
catch (Exception ex)
{

View File

@ -25,9 +25,9 @@
<td>
<select id="container" class="form-control" @bind="@_containerType">
<option value="-">&lt;Inherit From Page Or Site&gt;</option>
@foreach (KeyValuePair<string, string> container in _containers)
@foreach (var container in _containers)
{
<option value="@container.Key">@container.Value</option>
<option value="@container.TypeName">@container.Name</option>
}
</select>
</td>
@ -63,7 +63,7 @@
}
</TabPanel>
<TabPanel Name="Permissions">
@if (_containers != null)
@if (_permissions != null)
{
<table class="table table-borderless">
<tr>
@ -85,12 +85,12 @@
<NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink>
@code {
private Dictionary<string, string> _containers;
private List<ThemeControl> _containers = new List<ThemeControl>();
private string _title;
private string _containerType;
private string _allPages = "false";
private string _permissionNames = "";
private string _permissions;
private string _permissions = null;
private string _pageId;
private PermissionGrid _permissionGrid;
private Type _settingsModuleType;
@ -105,7 +105,7 @@
protected override async Task OnInitializedAsync()
{
_title = ModuleState.Title;
_containers = ThemeService.GetContainerTypes(await ThemeService.GetThemesAsync());
_containers = ThemeService.GetContainerControls(await ThemeService.GetThemesAsync(), PageState.Page.ThemeType);
_containerType = ModuleState.ContainerType;
if (!string.IsNullOrEmpty(PageState.Page.DefaultContainerType) && _containerType == PageState.Page.DefaultContainerType)
{

View File

@ -102,41 +102,44 @@
<td>
<select id="Theme" class="form-control" @onchange="(e => ThemeChanged(e))">
<option value="-">&lt;Inherit From Site&gt;</option>
@foreach (KeyValuePair<string, string> item in _themes)
@foreach (var theme in _themes)
{
if (item.Key == _themetype)
if (theme.TypeName == _themetype)
{
<option value="@item.Key" selected>@item.Value</option>
<option value="@theme.TypeName" selected>@theme.Name</option>
}
else
{
<option value="@item.Key">@item.Value</option>
<option value="@theme.TypeName">@theme.Name</option>
}
}
</select>
</td>
</tr>
<tr>
<td>
<Label For="Layout" HelpText="Select a layout for the page (if the selected theme supports it)">Layout: </Label>
</td>
<td>
<select id="Layout" class="form-control" @bind="@_layouttype">
<option value="-">&lt;Inherit From Site&gt;</option>
@foreach (KeyValuePair<string, string> panelayout in _panelayouts)
{
if (panelayout.Key == _layouttype)
@if (_layouts.Count > 0)
{
<tr>
<td>
<Label For="Layout" HelpText="Select a layout for the page (if the selected theme supports it)">Layout: </Label>
</td>
<td>
<select id="Layout" class="form-control" @bind="@_layouttype">
<option value="-">&lt;Inherit From Site&gt;</option>
@foreach (var layout in _layouts)
{
<option value="@panelayout.Key" selected>@panelayout.Value</option>
if (layout.TypeName == _layouttype)
{
<option value="@(layout.TypeName)" selected>@(layout.Name)</option>
}
else
{
<option value="@(layout.TypeName)">@(layout.Name)</option>
}
}
else
{
<option value="@panelayout.Key">@panelayout.Value</option>
}
}
</select>
</td>
</tr>
</select>
</td>
</tr>
}
<tr>
<td>
<Label For="defaultContainer" HelpText="Select the default container for the page">Default Container: </Label>
@ -144,9 +147,9 @@
<td>
<select id="defaultContainer" class="form-control" @bind="@_containertype">
<option value="-">&lt;Inherit From Site&gt;</option>
@foreach (KeyValuePair<string, string> container in _containers)
@foreach (var container in _containers)
{
<option value="@container.Key">@container.Value</option>
<option value="@container.TypeName">@container.Name</option>
}
</select>
</td>
@ -159,17 +162,6 @@
<input id="Icon" class="form-control" @bind="@_icon" />
</td>
</tr>
<tr>
<td>
<Label For="Default-Mode" HelpText="Select the default administration mode you want for this page">Default Mode? </Label>
</td>
<td>
<select id="Default-Mode" class="form-control" @bind="@_mode">
<option value="view">View Mode</option>
<option value="edit">Edit Mode</option>
</select>
</td>
</tr>
<tr>
<td>
<Label For="Personalizable" HelpText="Select whether you would like users to be able to personalize this page with their own content">Personalizable? </Label>
@ -199,10 +191,10 @@
<NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink>
@code {
private Dictionary<string, string> _themes;
private Dictionary<string, string> _panelayouts;
private Dictionary<string, string> _containers = new Dictionary<string, string>();
private List<Theme> _themeList;
private List<ThemeControl> _themes = new List<ThemeControl>();
private List<ThemeControl> _layouts = new List<ThemeControl>();
private List<ThemeControl> _containers = new List<ThemeControl>();
private List<Page> _pageList;
private string _name;
private string _title;
@ -214,7 +206,6 @@
private string _isnavigation = "True";
private string _url;
private string _ispersonalizable = "False";
private string _mode = "view";
private string _themetype = "-";
private string _layouttype = "-";
private string _containertype = "-";
@ -232,10 +223,7 @@
_pageList = PageState.Pages;
_children = PageState.Pages.Where(item => item.ParentId == null).ToList();
_themes = ThemeService.GetThemeTypes(_themeList);
_panelayouts = ThemeService.GetPaneLayoutTypes(_themeList, _themetype);
_containers = ThemeService.GetContainerTypes(_themeList);
_themes = ThemeService.GetThemeControls(_themeList);
_permissions = string.Empty;
}
catch (Exception ex)
@ -287,12 +275,16 @@
_themetype = (string)e.Value;
if (_themetype != "-")
{
_panelayouts = ThemeService.GetPaneLayoutTypes(_themeList, _themetype);
_layouts = ThemeService.GetLayoutControls(_themeList, _themetype);
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
}
else
{
_panelayouts = new Dictionary<string, string>();
_layouts = new List<ThemeControl>();
_containers = new List<ThemeControl>();
}
_layouttype = "-";
_containertype = "-";
StateHasChanged();
}
catch (Exception ex)
@ -307,7 +299,7 @@
Page page = null;
try
{
if (_name != string.Empty && !string.IsNullOrEmpty(_themetype) && (_panelayouts.Count == 0 || !string.IsNullOrEmpty(_layouttype)))
if (_name != string.Empty && !string.IsNullOrEmpty(_themetype) && (_layouts.Count == 0 || !string.IsNullOrEmpty(_layouttype)))
{
page = new Page();
page.SiteId = PageState.Page.SiteId;
@ -342,6 +334,12 @@
}
}
if (!PagePathIsUnique(page.Path, page.SiteId, _pageList))
{
AddModuleMessage($"A page with path {_path} already exists for the selected parent page. The page path needs to be unique for the selected parent.", MessageType.Warning);
return;
}
Page child;
switch (_insert)
{
@ -363,7 +361,6 @@
page.IsNavigation = (_isnavigation == null ? true : Boolean.Parse(_isnavigation));
page.Url = _url;
page.EditMode = (_mode == "edit" ? true : false);
page.ThemeType = (_themetype != "-") ? _themetype : string.Empty;
if (!string.IsNullOrEmpty(page.ThemeType) && page.ThemeType == PageState.Site.DefaultThemeType)
{
@ -403,4 +400,8 @@
}
}
private static bool PagePathIsUnique(string pagePath, int siteId, List<Page> existingPages)
{
return !existingPages.Any(page => page.SiteId == siteId && page.Path == pagePath);
}
}

View File

@ -113,41 +113,44 @@
<td>
<select id="Theme" class="form-control" @onchange="(e => ThemeChanged(e))">
<option value="-">&lt;Inherit From Site&gt;</option>
@foreach (KeyValuePair<string, string> item in _themes)
@foreach (var theme in _themes)
{
if (item.Key == _themetype)
if (theme.TypeName == _themetype)
{
<option value="@item.Key" selected>@item.Value</option>
<option value="@theme.TypeName" selected>@theme.Name</option>
}
else
{
<option value="@item.Key">@item.Value</option>
<option value="@theme.TypeName">@theme.Name</option>
}
}
</select>
</td>
</tr>
<tr>
<td>
<Label For="Layout" HelpText="Select a layout for the page (if the selected theme supports it)">Layout: </Label>
</td>
<td>
<select id="Layout" class="form-control" @bind="@_layouttype">
<option value="-">&lt;Inherit From Site&gt;</option>
@foreach (KeyValuePair<string, string> panelayout in _panelayouts)
{
if (panelayout.Key == _layouttype)
@if (_layouts.Count > 0)
{
<tr>
<td>
<Label For="Layout" HelpText="Select a layout for the page (if the selected theme supports it)">Layout: </Label>
</td>
<td>
<select id="Layout" class="form-control" @bind="@_layouttype">
<option value="-">&lt;Inherit From Site&gt;</option>
@foreach (var layout in _layouts)
{
<option value="@panelayout.Key" selected>@panelayout.Value</option>
if (layout.TypeName == _layouttype)
{
<option value="@(layout.TypeName)" selected>@(layout.Name)</option>
}
else
{
<option value="@(layout.TypeName)">@(layout.Name)</option>
}
}
else
{
<option value="@panelayout.Key">@panelayout.Value</option>
}
}
</select>
</td>
</tr>
</select>
</td>
</tr>
}
<tr>
<td>
<Label For="defaultContainer" HelpText="Select the default container for the page">Default Container: </Label>
@ -155,9 +158,9 @@
<td>
<select id="defaultContainer" class="form-control" @bind="@_containertype">
<option value="-">&lt;Inherit From Site&gt;</option>
@foreach (KeyValuePair<string, string> container in _containers)
@foreach (var container in _containers)
{
<option value="@container.Key">@container.Value</option>
<option value="@container.TypeName">@container.Name</option>
}
</select>
</td>
@ -170,17 +173,6 @@
<input id="Icon" class="form-control" @bind="@_icon" />
</td>
</tr>
<tr>
<td>
<Label For="Default-Mode" HelpText="Select the default administration mode you want for this page">Default Mode? </Label>
</td>
<td>
<select id="Default-Mode" class="form-control" @bind="@_mode">
<option value="view">View Mode</option>
<option value="edit">Edit Mode</option>
</select>
</td>
</tr>
<tr>
<td>
<Label For="Personalizable" HelpText="Select whether you would like users to be able to personalize this page with their own content">Personalizable? </Label>
@ -199,23 +191,26 @@
}
</TabPanel>
<TabPanel Name="Permissions">
<table class="table table-borderless">
<tr>
<td>
<PermissionGrid EntityName="@EntityNames.Page" Permissions="@_permissions" @ref="_permissionGrid" />
</td>
</tr>
</table>
@if (_permissions != null)
{
<table class="table table-borderless">
<tr>
<td>
<PermissionGrid EntityName="@EntityNames.Page" Permissions="@_permissions" @ref="_permissionGrid" />
</td>
</tr>
</table>
}
</TabPanel>
</TabStrip>
<button type="button" class="btn btn-success" @onclick="SavePage">Save</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink>
@code {
private Dictionary<string, string> _themes;
private Dictionary<string, string> _panelayouts;
private Dictionary<string, string> _containers = new Dictionary<string, string>();
private List<Theme> _themeList;
private List<ThemeControl> _themes = new List<ThemeControl>();
private List<ThemeControl> _layouts = new List<ThemeControl>();
private List<ThemeControl> _containers = new List<ThemeControl>();
private List<Page> _pageList;
private int _pageId;
private string _name;
@ -229,12 +224,11 @@
private string _isnavigation;
private string _url;
private string _ispersonalizable;
private string _mode;
private string _themetype = "-";
private string _layouttype = "-";
private string _containertype = "-";
private string _icon;
private string _permissions;
private string _permissions = null;
private string _createdby;
private DateTime _createdon;
private string _modifiedby;
@ -252,12 +246,11 @@
{
try
{
_themeList = await ThemeService.GetThemesAsync();
_pageList = PageState.Pages;
_children = PageState.Pages.Where(item => item.ParentId == null).ToList();
_themes = ThemeService.GetThemeTypes(_themeList);
_containers = ThemeService.GetContainerTypes(_themeList);
_themeList = await ThemeService.GetThemesAsync();
_themes = ThemeService.GetThemeControls(_themeList);
_pageId = Int32.Parse(PageState.QueryString["id"]);
var page = PageState.Pages.FirstOrDefault(item => item.PageId == _pageId);
@ -285,18 +278,18 @@
_isnavigation = page.IsNavigation.ToString();
_url = page.Url;
_ispersonalizable = page.IsPersonalizable.ToString();
_mode = (page.EditMode) ? "edit" : "view";
_themetype = page.ThemeType;
if (_themetype == PageState.Site.DefaultThemeType)
{
_themetype = "-";
}
_panelayouts = ThemeService.GetPaneLayoutTypes(_themeList, _themetype);
_layouts = ThemeService.GetLayoutControls(_themeList, page.ThemeType);
_layouttype = page.LayoutType;
if (_layouttype == PageState.Site.DefaultLayoutType)
{
_layouttype = "-";
}
_containers = ThemeService.GetContainerControls(_themeList, page.ThemeType);
_containertype = page.DefaultContainerType;
if (string.IsNullOrEmpty(_containertype))
{
@ -327,7 +320,7 @@
_children = new List<Page>();
if (_parentid == "-1")
{
foreach(Page p in PageState.Pages.Where(item => item.ParentId == null))
foreach (Page p in PageState.Pages.Where(item => item.ParentId == null))
{
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions))
{
@ -369,12 +362,16 @@
_themetype = (string)e.Value;
if (_themetype != "-")
{
_panelayouts = ThemeService.GetPaneLayoutTypes(_themeList, _themetype);
_layouts = ThemeService.GetLayoutControls(_themeList, _themetype);
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
}
else
{
_panelayouts = new Dictionary<string, string>();
_layouts = new List<ThemeControl>();
_containers = new List<ThemeControl>();
}
_layouttype = "-";
_containertype = "-";
StateHasChanged();
}
catch (Exception ex)
@ -423,6 +420,13 @@
page.Path = parent.Path + "/" + Utilities.GetFriendlyUrl(_path);
}
}
if (!PagePathIsUnique(page.Path, page.SiteId, page.PageId, _pageList))
{
AddModuleMessage($"A page with path {_path} already exists for the selected parent page. The page path needs to be unique for the selected parent.", MessageType.Warning);
return;
}
if (_insert != "=")
{
Page child;
@ -446,7 +450,6 @@
}
page.IsNavigation = (_isnavigation == null || Boolean.Parse(_isnavigation));
page.Url = _url;
page.EditMode = (_mode == "edit");
page.ThemeType = (_themetype != "-") ? _themetype : string.Empty;
if (!string.IsNullOrEmpty(page.ThemeType) && page.ThemeType == PageState.Site.DefaultThemeType)
{
@ -502,4 +505,9 @@
AddModuleMessage("Error Saving Page", MessageType.Error);
}
}
private static bool PagePathIsUnique(string pagePath, int siteId, int pageId, List<Page> existingPages)
{
return !existingPages.Any(page => page.SiteId == siteId && page.Path == pagePath && page.PageId != pageId);
}
}

View File

@ -9,8 +9,8 @@
<Pager Items="@PageState.Pages.Where(item => !item.IsDeleted)">
<Header>
<th>&nbsp;</th>
<th>&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>Name</th>
</Header>
<Row>

View File

@ -12,8 +12,8 @@ else
<Pager Items="@_profiles">
<Header>
<th>&nbsp;</th>
<th>&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>Name</th>
</Header>
<Row>

View File

@ -16,8 +16,8 @@
{
<Pager Items="@_pages">
<Header>
<th>&nbsp;</th>
<th>&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>Name</th>
<th>Deleted By</th>
<th>Deleted On</th>
@ -42,8 +42,8 @@
{
<Pager Items="@_modules">
<Header>
<th>&nbsp;</th>
<th>&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>Page</th>
<th>Module</th>
<th>Deleted By</th>

View File

@ -12,9 +12,9 @@ else
<Pager Items="@_roles">
<Header>
<th>&nbsp;</th>
<th>&nbsp;</th>
<th>&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>Name</th>
</Header>
<Row>

View File

@ -7,7 +7,7 @@
@inject IThemeService ThemeService
@inject ISettingService SettingService
@if (_themes != null)
@if (_initialized)
{
<table class="table table-borderless">
<tr>
@ -56,45 +56,48 @@
</td>
<td>
<select id="defaultTheme" class="form-control" @onchange="(e => ThemeChanged(e))">
<option value="">&lt;Select Theme&gt;</option>
@foreach (KeyValuePair<string, string> item in _themes)
<option value="-">&lt;Select Theme&gt;</option>
@foreach (var theme in _themes)
{
if (item.Key == _themetype)
if (theme.TypeName == _themetype)
{
<option value="@item.Key" selected>@item.Value</option>
<option value="@theme.TypeName" selected>@theme.Name</option>
}
else
{
<option value="@item.Key">@item.Value</option>
<option value="@theme.TypeName">@theme.Name</option>
}
}
</select>
</td>
</tr>
<tr>
<td>
<Label For="defaultLayout" HelpText="Select the sites default layout">Default Layout: </Label>
</td>
<td>
<select id="defaultLayout" class="form-control" @bind="@_layouttype">
<option value="">&lt;Select Layout&gt;</option>
@foreach (KeyValuePair<string, string> panelayout in _panelayouts)
{
<option value="@panelayout.Key">@panelayout.Value</option>
}
</select>
</td>
</tr>
@if (_layouts.Count > 0)
{
<tr>
<td>
<Label For="defaultLayout" HelpText="Select the sites default layout">Default Layout: </Label>
</td>
<td>
<select id="defaultLayout" class="form-control" @bind="@_layouttype">
<option value="-">&lt;Select Layout&gt;</option>
@foreach (var layout in _layouts)
{
<option value="@(layout.TypeName)">@(layout.Name)</option>
}
</select>
</td>
</tr>
}
<tr>
<td>
<Label For="defaultContainer" HelpText="Select the default container for the site">Default Container: </Label>
</td>
<td>
<select id="defaultContainer" class="form-control" @bind="@_containertype">
<option value="">&lt;Select Container&gt;</option>
@foreach (KeyValuePair<string, string> container in _containers)
<option value="-">&lt;Select Container&gt;</option>
@foreach (var container in _containers)
{
<option value="@container.Key">@container.Value</option>
<option value="@container.TypeName">@container.Name</option>
}
</select>
</td>
@ -208,10 +211,11 @@
}
@code {
private Dictionary<string, string> _themes;
private Dictionary<string, string> _panelayouts;
private Dictionary<string, string> _containers;
private bool _initialized = false;
private List<Theme> _themeList;
private List<ThemeControl> _themes = new List<ThemeControl>();
private List<ThemeControl> _layouts = new List<ThemeControl>();
private List<ThemeControl> _containers = new List<ThemeControl>();
private string _name = string.Empty;
private List<Tenant> _tenantList;
private string _tenant = string.Empty;
@ -221,9 +225,9 @@
private FileManager _logofilemanager;
private int _faviconfileid = -1;
private FileManager _faviconfilemanager;
private string _themetype;
private string _layouttype;
private string _containertype;
private string _themetype = "-";
private string _layouttype = "-";
private string _containertype = "-";
private string _allowregistration;
private string _smtphost = string.Empty;
private string _smtpport = string.Empty;
@ -271,9 +275,11 @@
_faviconfileid = site.FaviconFileId.Value;
}
_themes = ThemeService.GetThemeControls(_themeList);
_themetype = site.DefaultThemeType;
_panelayouts = ThemeService.GetPaneLayoutTypes(_themeList, _themetype);
_layouts = ThemeService.GetLayoutControls(_themeList, _themetype);
_layouttype = site.DefaultLayoutType;
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
_containertype = site.DefaultContainerType;
_allowregistration = site.AllowRegistration.ToString();
@ -313,10 +319,9 @@
_deletedby = site.DeletedBy;
_deletedon = site.DeletedOn;
_isdeleted = site.IsDeleted.ToString();
}
_themes = ThemeService.GetThemeTypes(_themeList);
_containers = ThemeService.GetContainerTypes(_themeList);
_initialized = true;
}
}
catch (Exception ex)
{
@ -330,14 +335,18 @@
try
{
_themetype = (string)e.Value;
if (_themetype != string.Empty)
if (_themetype != "-")
{
_panelayouts = ThemeService.GetPaneLayoutTypes(_themeList, _themetype);
_layouts = ThemeService.GetLayoutControls(_themeList, _themetype);
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
}
else
{
_panelayouts = new Dictionary<string, string>();
_layouts = new List<ThemeControl>();
_containers = new List<ThemeControl>();
}
_layouttype = "-";
_containertype = "-";
StateHasChanged();
}
catch (Exception ex)
@ -351,7 +360,7 @@
{
try
{
if (_name != string.Empty && _urls != string.Empty && !string.IsNullOrEmpty(_themetype) && (_panelayouts.Count == 0 || !string.IsNullOrEmpty(_layouttype)) && !string.IsNullOrEmpty(_containertype))
if (_name != string.Empty && _urls != string.Empty && _themetype != "-" && (_layouts.Count == 0 || _layouttype != "-") && _containertype != "-")
{
var unique = true;
foreach (string name in _urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
@ -376,7 +385,7 @@
}
site.DefaultThemeType = _themetype;
site.DefaultLayoutType = (_layouttype == null ? string.Empty : _layouttype);
site.DefaultLayoutType = (_layouttype == "-" ? string.Empty : _layouttype);
site.DefaultContainerType = _containertype;
site.AllowRegistration = (_allowregistration == null ? true : Boolean.Parse(_allowregistration));
site.IsDeleted = (_isdeleted == null ? true : Boolean.Parse(_isdeleted));

View File

@ -38,38 +38,41 @@ else
</td>
<td>
<select id="defaultTheme" class="form-control" @onchange="(e => ThemeChanged(e))">
<option value="">&lt;Select Theme&gt;</option>
@foreach (KeyValuePair<string, string> item in _themes)
<option value="-">&lt;Select Theme&gt;</option>
@foreach (var theme in _themes)
{
<option value="@item.Key">@item.Value</option>
}
</select>
</td>
</tr>
<tr>
<td>
<Label For="defaultLayout" HelpText="Select the default layout for the site">Default Layout: </Label>
</td>
<td>
<select id="defaultLayout" class="form-control" @bind="@_layouttype">
<option value="">&lt;Select Layout&gt;</option>
@foreach (KeyValuePair<string, string> panelayout in _panelayouts)
{
<option value="@panelayout.Key">@panelayout.Value</option>
<option value="@theme.TypeName">@theme.Name</option>
}
</select>
</td>
</tr>
@if (_layouts.Count > 0)
{
<tr>
<td>
<Label For="defaultLayout" HelpText="Select the default layout for the site">Default Layout: </Label>
</td>
<td>
<select id="defaultLayout" class="form-control" @bind="@_layouttype">
<option value="-">&lt;Select Layout&gt;</option>
@foreach (var layout in _layouts)
{
<option value="@(layout.TypeName)">@(layout.Name)</option>
}
</select>
</td>
</tr>
}
<tr>
<td>
<Label For="defaultContainer" HelpText="Select the default container for the site">Default Container: </Label>
</td>
<td>
<select id="defaultContainer" class="form-control" @bind="@_containertype">
<option value="">&lt;Select Container&gt;</option>
@foreach (KeyValuePair<string, string> container in _containers)
<option value="-">&lt;Select Container&gt;</option>
@foreach (var container in _containers)
{
<option value="@container.Key">@container.Value</option>
<option value="@container.TypeName">@container.Name</option>
}
</select>
</td>
@ -80,7 +83,7 @@ else
</td>
<td>
<select id="siteTemplate" class="form-control" @bind="@_sitetemplatetype">
<option value="">&lt;Select Site Template&gt;</option>
<option value="-">&lt;Select Site Template&gt;</option>
@foreach (SiteTemplate siteTemplate in _siteTemplates)
{
<option value="@siteTemplate.TypeName">@siteTemplate.Name</option>
@ -198,11 +201,11 @@ else
}
@code {
private Dictionary<string, string> _themes = new Dictionary<string, string>();
private Dictionary<string, string> _panelayouts = new Dictionary<string, string>();
private Dictionary<string, string> _containers = new Dictionary<string, string>();
private List<SiteTemplate> _siteTemplates;
private List<Theme> _themeList;
private List<ThemeControl> _themes = new List<ThemeControl>();
private List<ThemeControl> _layouts = new List<ThemeControl>();
private List<ThemeControl> _containers = new List<ThemeControl>();
private List<SiteTemplate> _siteTemplates;
private List<Tenant> _tenants;
private string _tenantid = "-";
@ -218,20 +221,19 @@ else
private string _name = string.Empty;
private string _urls = string.Empty;
private string _themetype = string.Empty;
private string _layouttype = string.Empty;
private string _containertype = string.Empty;
private string _sitetemplatetype = string.Empty;
private string _themetype = "-";
private string _layouttype = "-";
private string _containertype = "-";
private string _sitetemplatetype = "-";
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
protected override async Task OnInitializedAsync()
{
_themeList = await ThemeService.GetThemesAsync();
_tenants = await TenantService.GetTenantsAsync();
_urls = PageState.Alias.Name;
_themes = ThemeService.GetThemeTypes(_themeList);
_containers = ThemeService.GetContainerTypes(_themeList);
_themeList = await ThemeService.GetThemesAsync();
_themes = ThemeService.GetThemeControls(_themeList);
_siteTemplates = await SiteTemplateService.GetSiteTemplatesAsync();
}
@ -263,15 +265,18 @@ else
try
{
_themetype = (string)e.Value;
if (_themetype != string.Empty)
if (_themetype != "-")
{
_panelayouts = ThemeService.GetPaneLayoutTypes(_themeList, _themetype);
_layouts = ThemeService.GetLayoutControls(_themeList, _themetype);
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
}
else
{
_panelayouts = new Dictionary<string, string>();
_layouts = new List<ThemeControl>();
_containers = new List<ThemeControl>();
}
_layouttype = "-";
_containertype = "-";
StateHasChanged();
}
catch (Exception ex)
@ -283,7 +288,7 @@ else
private async Task SaveSite()
{
if (_tenantid != "-" && _name != string.Empty && _urls != string.Empty && !string.IsNullOrEmpty(_themetype) && (_panelayouts.Count == 0 || !string.IsNullOrEmpty(_layouttype)) && !string.IsNullOrEmpty(_containertype) && !string.IsNullOrEmpty(_sitetemplatetype))
if (_tenantid != "-" && _name != string.Empty && _urls != string.Empty && _themetype != "-" && (_layouts.Count == 0 || _layouttype != "-") && _containertype != "-" && _sitetemplatetype != "-")
{
var duplicates = new List<string>();
var aliases = await AliasService.GetAliasesAsync();

View File

@ -6,7 +6,7 @@
@inject IAliasService AliasService
@inject IThemeService ThemeService
@if (_themes != null)
@if (_initialized)
{
<table class="table table-borderless">
<tr>
@ -39,45 +39,48 @@
</td>
<td>
<select id="defaultTheme" class="form-control" @onchange="(e => ThemeChanged(e))">
<option value="">&lt;Select Theme&gt;</option>
@foreach (KeyValuePair<string, string> item in _themes)
<option value="-">&lt;Select Theme&gt;</option>
@foreach (var theme in _themes)
{
if (item.Key == _themetype)
if (theme.TypeName == _themetype)
{
<option value="@item.Key" selected>@item.Value</option>
<option value="@theme.TypeName" selected>@theme.Name</option>
}
else
{
<option value="@item.Key">@item.Value</option>
<option value="@theme.TypeName">@theme.Name</option>
}
}
</select>
</td>
</tr>
<tr>
<td>
<Label For="defaultLayout" HelpText="Select the default layout for the site">Default Layout: </Label>
</td>
<td>
<select id="defaultLayout" class="form-control" @bind="@_layouttype">
<option value="">&lt;Select Layout&gt;</option>
@foreach (KeyValuePair<string, string> panelayout in _panelayouts)
{
<option value="@panelayout.Key">@panelayout.Value</option>
}
</select>
</td>
</tr>
@if (_layouts.Count > 0)
{
<tr>
<td>
<Label For="defaultLayout" HelpText="Select the default layout for the site">Default Layout: </Label>
</td>
<td>
<select id="defaultLayout" class="form-control" @bind="@_layouttype">
<option value="-">&lt;Select Layout&gt;</option>
@foreach (var layout in _layouts)
{
<option value="@(layout.TypeName)">@(layout.Name)</option>
}
</select>
</td>
</tr>
}
<tr>
<td>
<Label For="defaultContainer" HelpText="Select the default container for the site">Default Container: </Label>
</td>
<td>
<select id="defaultIdea" class="form-control" @bind="@_containertype">
<option value="">&lt;Select Container&gt;</option>
@foreach (KeyValuePair<string, string> container in _containers)
<option value="-">&lt;Select Container&gt;</option>
@foreach (var container in _containers)
{
<option value="@container.Key">@container.Value</option>
<option value="@container.TypeName">@container.Name</option>
}
</select>
</td>
@ -103,11 +106,12 @@
}
@code {
private Dictionary<string, string> _themes;
private Dictionary<string, string> _panelayouts;
private Dictionary<string, string> _containers;
private Alias _alias;
private bool _initialized = false;
private List<Theme> _themeList;
private List<ThemeControl> _themes = new List<ThemeControl>();
private List<ThemeControl> _layouts = new List<ThemeControl>();
private List<ThemeControl> _containers = new List<ThemeControl>();
private Alias _alias;
private string _name = string.Empty;
private List<Tenant> _tenantList;
private string _tenant = string.Empty;
@ -147,9 +151,11 @@
_urls += alias.Name + "\n";
}
_themes = ThemeService.GetThemeControls(_themeList);
_themetype = site.DefaultThemeType;
_panelayouts = ThemeService.GetPaneLayoutTypes(_themeList, _themetype);
_layouts = ThemeService.GetLayoutControls(_themeList, _themetype);
_layouttype = site.DefaultLayoutType;
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
_containertype = site.DefaultContainerType;
_createdby = site.CreatedBy;
_createdon = site.CreatedOn;
@ -158,10 +164,9 @@
_deletedby = site.DeletedBy;
_deletedon = site.DeletedOn;
_isdeleted = site.IsDeleted.ToString();
}
_themes = ThemeService.GetThemeTypes(_themeList);
_containers = ThemeService.GetContainerTypes(_themeList);
_initialized = true;
}
}
catch (Exception ex)
{
@ -175,15 +180,18 @@
try
{
_themetype = (string)e.Value;
if (_themetype != string.Empty)
if (_themetype != "-")
{
_panelayouts = ThemeService.GetPaneLayoutTypes(_themeList, _themetype);
_layouts = ThemeService.GetLayoutControls(_themeList, _themetype);
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
}
else
{
_panelayouts = new Dictionary<string, string>();
_layouts = new List<ThemeControl>();
_containers = new List<ThemeControl>();
}
_layouttype = "-";
_containertype = "-";
StateHasChanged();
}
catch (Exception ex)
@ -197,7 +205,7 @@
{
try
{
if (_name != string.Empty && _urls != string.Empty && !string.IsNullOrEmpty(_themetype) && (_panelayouts.Count == 0 || !string.IsNullOrEmpty(_layouttype)) && !string.IsNullOrEmpty(_containertype))
if (_name != string.Empty && _urls != string.Empty && _themetype != "-" && (_layouts.Count == 0 || _layouttype != "-") && _containertype != "-")
{
var unique = true;
foreach (string name in _urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))

View File

@ -14,8 +14,8 @@ else
<Pager Items="@_sites">
<Header>
<th>&nbsp;</th>
<th>&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>Name</th>
</Header>
<Row>

View File

@ -11,8 +11,8 @@ else
{
<Pager Items="@tenants">
<Header>
<th>&nbsp;</th>
<th>&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>Name</th>
</Header>
<Row>

View File

@ -7,43 +7,43 @@
@if (_packages != null)
{
<TabStrip>
@if (_packages.Count > 0)
{
<TabPanel Name="Download">
<ModuleMessage Type="MessageType.Info" Message="Download one or more themes from the list below. Once you are ready click Install to complete the installation."></ModuleMessage>
<Pager Items="@_packages">
<Header>
<th>Name</th>
<th>Version</th>
<th></th>
</Header>
<Row>
<td>@context.Name</td>
<td>@context.Version</td>
<td>
<button type="button" class="btn btn-primary" @onclick=@(async () => await DownloadTheme(context.PackageId, context.Version))>Download</button>
</td>
</Row>
</Pager>
</TabPanel>
}
<TabPanel Name="Upload">
<table class="table table-borderless">
<tr>
<td>
<Label HelpText="Upload one or more theme packages. Once they are uploaded click Install to complete the installation.">Theme: </Label>
</td>
<td>
<FileManager Filter="nupkg" ShowFiles="false" Folder="Themes" UploadMultiple="@true" />
</td>
</tr>
</table>
</TabPanel>
</TabStrip>
<TabStrip>
@if (_packages.Count > 0)
{
<TabPanel Name="Download">
<ModuleMessage Type="MessageType.Info" Message="Download one or more themes from the list below. Once you are ready click Install to complete the installation."></ModuleMessage>
<Pager Items="@_packages">
<Header>
<th>Name</th>
<th>Version</th>
<th style="width: 1px;"></th>
</Header>
<Row>
<td>@context.Name</td>
<td>@context.Version</td>
<td>
<button type="button" class="btn btn-primary" @onclick=@(async () => await DownloadTheme(context.PackageId, context.Version))>Download</button>
</td>
</Row>
</Pager>
</TabPanel>
}
<TabPanel Name="Upload">
<table class="table table-borderless">
<tr>
<td>
<Label HelpText="Upload one or more theme packages. Once they are uploaded click Install to complete the installation.">Theme: </Label>
</td>
<td>
<FileManager Filter="nupkg" ShowFiles="false" Folder="Themes" UploadMultiple="@true" />
</td>
</tr>
</table>
</TabPanel>
</TabStrip>
<button type="button" class="btn btn-success" @onclick="InstallThemes">Install</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink>
<button type="button" class="btn btn-success" @onclick="InstallThemes">Install</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink>
}
@code {
@ -77,8 +77,10 @@
{
try
{
ShowProgressIndicator();
var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 10);
await ThemeService.InstallThemesAsync();
NavigationManager.NavigateTo(NavigateUrl());
}
catch (Exception ex)
{
@ -101,4 +103,4 @@
AddModuleMessage("Error Downloading Theme", MessageType.Error);
}
}
}
}

View File

@ -1,10 +1,11 @@
@namespace Oqtane.Modules.Admin.Themes
@using System.Net
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IThemeService ThemeService
@inject IPackageService PackageService
@if (themes == null)
@if (_themes == null)
{
<p><em>Loading...</em></p>
}
@ -12,68 +13,103 @@ else
{
<ActionLink Action="Add" Text="Install Theme" />
<Pager Items="@themes">
<Pager Items="@_themes">
<Header>
<th>&nbsp;</th>
<th>Name</th>
<th>Version</th>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th scope="col">Name</th>
<th scope="col">Version</th>
<th>&nbsp;</th>
</Header>
<Row>
<td><ActionLink Action="View" Parameters="@($"name=" + WebUtility.UrlEncode(context.ThemeName))" /></td>
<td>
@if (context.AssemblyName != "Oqtane.Client")
{
{
<ActionDialog Header="Delete Theme" Message="@("Are You Sure You Wish To Delete The " + context.Name + " Theme?")" Action="Delete" Security="SecurityAccessLevel.Host" Class="btn btn-danger" OnClick="@(async () => await DeleteTheme(context))" />
}
}
</td>
<td>@context.Name</td>
<td>@context.Version</td>
<td>
@if (UpgradeAvailable(context.ThemeName, context.Version))
{
{
<button type="button" class="btn btn-success" @onclick=@(async () => await DownloadTheme(context.ThemeName, context.Version))>Upgrade</button>
}
}
</td>
<td></td>
</Row>
</Pager>
}
@code {
private List<Theme> themes;
private List<Package> packages;
private List<Theme> _themes;
private List<Package> _packages;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
protected override async Task OnInitializedAsync()
{
themes = await ThemeService.GetThemesAsync();
packages = await PackageService.GetPackagesAsync("module");
try
{
_themes = await ThemeService.GetThemesAsync();
_packages = await PackageService.GetPackagesAsync("theme");
}
catch (Exception ex)
{
if (_themes == null)
{
await logger.LogError(ex, "Error Loading Themes {Error}", ex.Message);
AddModuleMessage("Error Loading Themes", MessageType.Error);
}
}
}
private bool UpgradeAvailable(string themename, string version)
{
var upgradeavailable = false;
var package = packages.Where(item => item.PackageId == Utilities.GetTypeName(themename)).FirstOrDefault();
if (package != null)
if (_packages != null)
{
upgradeavailable = (Version.Parse(package.Version).CompareTo(Version.Parse(version)) > 0);
var package = _packages.Where(item => item.PackageId == Utilities.GetTypeName(themename)).FirstOrDefault();
if (package != null)
{
upgradeavailable = (Version.Parse(package.Version).CompareTo(Version.Parse(version)) > 0);
}
}
return upgradeavailable;
}
private async Task DownloadTheme(string themename, string version)
{
await PackageService.DownloadPackageAsync(themename, version, "Themes");
await logger.LogInformation("Theme Downloaded {ThemeName} {Version}", themename, version);
await ThemeService.InstallThemesAsync();
NavigationManager.NavigateTo(NavigateUrl());
try
{
await PackageService.DownloadPackageAsync(themename, version, "Themes");
await logger.LogInformation("Theme Downloaded {ThemeName} {Version}", themename, version);
ShowProgressIndicator();
var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 10);
await ThemeService.InstallThemesAsync();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Downloading Theme {ThemeName} {Version} {Error}", themename, version, ex.Message);
AddModuleMessage("Error Downloading Theme", MessageType.Error);
}
}
private async Task DeleteTheme(Theme Theme)
{
await ThemeService.DeleteThemeAsync(Theme.ThemeName);
await logger.LogInformation("Theme Deleted {Theme}", Theme);
NavigationManager.NavigateTo(NavigateUrl());
try
{
ShowProgressIndicator();
var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 10);
await ThemeService.DeleteThemeAsync(Theme.ThemeName);
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Deleting Theme {Theme} {Error}", Theme, ex.Message);
AddModuleMessage("Error Deleting Theme", MessageType.Error);
}
}
}

View File

@ -0,0 +1,101 @@
@namespace Oqtane.Modules.Admin.Themes
@using System.Net
@inherits ModuleBase
@inject IThemeService ThemeService
@inject NavigationManager NavigationManager
<table class="table table-borderless">
<tr>
<td>
<Label For="name" HelpText="The name of the theme">Name: </Label>
</td>
<td>
<input id="name" class="form-control" @bind="@_name" disabled />
</td>
</tr>
<tr>
<td>
<Label For="themename" HelpText="The internal name of the module">Internal Name: </Label>
</td>
<td>
<input id="themename" class="form-control" @bind="@_themeName" disabled />
</td>
</tr>
<tr>
<td>
<Label For="version" HelpText="The version of the thene">Version: </Label>
</td>
<td>
<input id="version" class="form-control" @bind="@_version" disabled />
</td>
</tr>
<tr>
<td>
<Label For="owner" HelpText="The owner or creator of the theme">Owner: </Label>
</td>
<td>
<input id="owner" class="form-control" @bind="@_owner" disabled />
</td>
</tr>
<tr>
<td>
<Label For="url" HelpText="The reference url of the theme">Reference Url: </Label>
</td>
<td>
<input id="url" class="form-control" @bind="@_url" disabled />
</td>
</tr>
<tr>
<td>
<Label For="contact" HelpText="The contact for the theme">Contact: </Label>
</td>
<td>
<input id="contact" class="form-control" @bind="@_contact" disabled />
</td>
</tr>
<tr>
<td>
<Label For="license" HelpText="The license of the theme">License: </Label>
</td>
<td>
<textarea id="license" class="form-control" @bind="@_license" rows="5" disabled></textarea>
</td>
</tr>
</table>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink>
@code {
private string _themeName = "";
private string _name;
private string _version;
private string _owner = "";
private string _url = "";
private string _contact = "";
private string _license = "";
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
protected override async Task OnInitializedAsync()
{
try
{
_themeName = WebUtility.UrlDecode(PageState.QueryString["name"]);
var themes = await ThemeService.GetThemesAsync();
var theme = themes.FirstOrDefault(item => item.ThemeName == _themeName);
if (theme != null)
{
_name = theme.Name;
_version = theme.Version;
_owner = theme.Owner;
_url = theme.Url;
_contact = theme.Contact;
_license = theme.License;
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading Theme {ThemeName} {Error}", _themeName, ex.Message);
AddModuleMessage("Error Loading Theme", MessageType.Error);
}
}
}

View File

@ -7,32 +7,32 @@
@if (_package != null)
{
<TabStrip>
<TabPanel Name="Download">
@if (_upgradeavailable)
{
<ModuleMessage Type="MessageType.Info" Message="Select The Upgrade Button To Install a New Framework Version"></ModuleMessage>
@("Framework") @_package.Version <button type="button" class="btn btn-success" @onclick=@(async () => await Download(Constants.PackageId, Constants.Version))>Upgrade</button>
}
else
{
<ModuleMessage Type="MessageType.Info" Message="Framework Is Already Up To Date"></ModuleMessage>
}
</TabPanel>
<TabPanel Name="Upload">
<table class="table table-borderless">
<tr>
<td>
<Label HelpText="Upload a framework package and select Install to complete the installation">Framework: </Label>
</td>
<td>
<FileManager Filter="nupkg" ShowFiles="false" Folder="Framework" />
</td>
</tr>
</table>
<button type="button" class="btn btn-success" @onclick="Upgrade">Install</button>
</TabPanel>
</TabStrip>
<TabStrip>
<TabPanel Name="Download">
@if (_upgradeavailable)
{
<ModuleMessage Type="MessageType.Info" Message="Select The Upgrade Button To Install a New Framework Version"></ModuleMessage>
<button type="button" class="btn btn-success" @onclick=@(async () => await Download(Constants.PackageId, @_package.Version))>Upgrade To @_package.Version</button>
}
else
{
<ModuleMessage Type="MessageType.Info" Message="Framework Is Already Up To Date"></ModuleMessage>
}
</TabPanel>
<TabPanel Name="Upload">
<table class="table table-borderless">
<tr>
<td>
<Label HelpText="Upload a framework package and select Install to complete the installation">Framework: </Label>
</td>
<td>
<FileManager Filter="nupkg" ShowFiles="false" Folder="Framework" />
</td>
</tr>
</table>
<button type="button" class="btn btn-success" @onclick="Upgrade">Install</button>
</TabPanel>
</TabStrip>
}
@code {
@ -43,28 +43,58 @@
protected override async Task OnInitializedAsync()
{
List<Package> packages = await PackageService.GetPackagesAsync("framework");
_package = packages.FirstOrDefault();
if (_package != null)
try
{
_upgradeavailable = (Version.Parse(_package.Version).CompareTo(Version.Parse(Constants.Version)) > 0);
List<Package> packages = await PackageService.GetPackagesAsync("framework");
if (packages != null)
{
_package = packages.FirstOrDefault();
if (_package != null)
{
_upgradeavailable = (Version.Parse(_package.Version).CompareTo(Version.Parse(Constants.Version)) > 0);
}
else
{
_package = new Package { Name = Constants.PackageId, Version = Constants.Version };
}
}
}
else
catch
{
_package = new Package { Name = Constants.PackageId, Version = Constants.Version };
// can be caused by no network connection
}
}
private async Task Upgrade()
{
await InstallationService.Upgrade();
NavigationManager.NavigateTo(NavigateUrl());
try
{
ShowProgressIndicator();
var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 10);
await InstallationService.Upgrade();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Executing Upgrade {Error}", ex.Message);
AddModuleMessage("Error Executing Upgrade", MessageType.Error);
}
}
private async Task Download(string packageid, string version)
{
await PackageService.DownloadPackageAsync(packageid, version, "Framework");
await InstallationService.Upgrade();
NavigationManager.NavigateTo(NavigateUrl());
try
{
await PackageService.DownloadPackageAsync(packageid, version, "Framework");
ShowProgressIndicator();
var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 10);
await InstallationService.Upgrade();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Downloading Framework {Error}", ex.Message);
AddModuleMessage("Error Downloading Framework", MessageType.Error);
}
}
}

View File

@ -1,7 +1,7 @@
@namespace Oqtane.Modules.Admin.UserProfile
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IUserRoleService UserRoleService
@inject IUserService UserService
@inject INotificationService NotificationService
@if (PageState.User != null)
@ -9,19 +9,10 @@
<table class="table table-borderless">
<tr>
<td>
<Label For="to" HelpText="Select the user it is going to">To: </Label>
<Label For="to" HelpText="Enter the username you wish to send a message to">To: </Label>
</td>
<td>
<select id="to" class="form-control" @bind="@userid">
<option value="-1">&lt;Select User&gt;</option>
@if (userroles != null)
{
foreach (UserRole userrole in userroles)
{
<option value="@userrole.UserId">@userrole.User.DisplayName</option>
}
}
</select>
<input id="to" class="form-control" @bind="@username" />
</td>
</tr>
<tr>
@ -46,8 +37,7 @@
}
@code {
private List<UserRole> userroles;
private string userid = "-1";
private string username = "";
private string subject = "";
private string body = "";
@ -55,41 +45,36 @@
public override string Title => "Send Notification";
protected override async Task OnInitializedAsync()
{
try
{
userroles = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId);
userroles = userroles.Where(item => item.Role.Name == Constants.RegisteredRole || item.Role.Name == Constants.HostRole)
.OrderBy(item => item.User.DisplayName).ToList();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading Users {Error}", ex.Message);
AddModuleMessage("Error Loading Users", MessageType.Error);
}
}
private async Task Send()
{
var notification = new Notification();
try
{
notification.SiteId = PageState.Site.SiteId;
notification.FromUserId = PageState.User.UserId;
notification.ToUserId = int.Parse(userid);
notification.ToEmail = "";
notification.Subject = subject;
notification.Body = body;
notification.ParentId = null;
notification.CreatedOn = DateTime.UtcNow;
notification.IsDelivered = false;
notification.DeliveredOn = null;
notification = await NotificationService.AddNotificationAsync(notification);
await logger.LogInformation("Notification Created {Notification}", notification);
NavigationManager.NavigateTo(NavigateUrl());
var user = await UserService.GetUserAsync(username, PageState.Site.SiteId);
if (user != null)
{
notification.SiteId = PageState.Site.SiteId;
notification.FromUserId = PageState.User.UserId;
notification.FromDisplayName = PageState.User.DisplayName;
notification.FromEmail = PageState.User.Email;
notification.ToUserId = user.UserId;
notification.ToDisplayName = user.DisplayName;
notification.ToEmail = user.Email;
notification.Subject = subject;
notification.Body = body;
notification.ParentId = null;
notification.CreatedOn = DateTime.UtcNow;
notification.IsDelivered = false;
notification.DeliveredOn = null;
notification.SendOn = DateTime.UtcNow;
notification = await NotificationService.AddNotificationAsync(notification);
await logger.LogInformation("Notification Created {Notification}", notification);
NavigationManager.NavigateTo(NavigateUrl());
}
else
{
AddModuleMessage("User Does Not Exist. Please Verify That The Username Provided Is Correct.", MessageType.Warning);
}
}
catch (Exception ex)
{

View File

@ -32,7 +32,7 @@ else
<label for="Name" class="control-label">Password: </label>
</td>
<td>
<input type="password" class="form-control" @bind="@password" />
<input type="password" class="form-control" @bind="@password" autocomplete="new-password" />
</td>
</tr>
<tr>
@ -40,7 +40,7 @@ else
<label for="Name" class="control-label">Confirm Password: </label>
</td>
<td>
<input type="password" class="form-control" @bind="@confirm" />
<input type="password" class="form-control" @bind="@confirm" autocomplete="new-password" />
</td>
</tr>
<tr>
@ -73,7 +73,7 @@ else
}
</TabPanel>
<TabPanel Name="Profile">
@if (profiles != null)
@if (profiles != null && settings != null)
{
<table class="table table-borderless">
@foreach (Profile profile in profiles)
@ -111,8 +111,8 @@ else
{
<Pager Items="@notifications">
<Header>
<th>&nbsp;</th>
<th>&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>From</th>
<th>Subject</th>
<th>Received</th>
@ -120,13 +120,22 @@ else
<Row>
<td><ActionLink Action="View" Parameters="@($"id=" + context.NotificationId.ToString())" Security="SecurityAccessLevel.View" EditMode="false" /></td>
<td><ActionDialog Header="Delete Notification" Message="@("Are You Sure You Wish To Delete This Notification?")" Action="Delete" Security="SecurityAccessLevel.View" Class="btn btn-danger" OnClick="@(async () => await Delete(context))" EditMode="false" /></td>
<td>@(context.FromUser == null ? "System" : context.FromUser.DisplayName)</td>
<td>@context.FromDisplayName</td>
<td>@context.Subject</td>
<td>@context.CreatedOn</td>
</Row>
<Detail>
<td colspan="2"></td>
<td colspan="3">@(context.Body.Length > 100 ? context.Body.Substring(0, 100) : context.Body)</td>
<td colspan="3">
@{
string input = "___";
if (context.Body.Contains(input)){
context.Body = context.Body.Split(input)[0];
context.Body = context.Body.Replace("\n", "");
context.Body = context.Body.Replace("\r", "");
} }
@(context.Body.Length > 100 ? (context.Body.Substring(0, 97) + "...") : context.Body)
</td>
</Detail>
</Pager>
}
@ -143,13 +152,22 @@ else
<Row>
<td><ActionLink Action="View" Parameters="@($"id=" + context.NotificationId.ToString())" Security="SecurityAccessLevel.View" EditMode="false" /></td>
<td><ActionDialog Header="Delete Notification" Message="@("Are You Sure You Wish To Delete This Notification?")" Action="Delete" Security="SecurityAccessLevel.View" Class="btn btn-danger" OnClick="@(async () => await Delete(context))" EditMode="false" /></td>
<td>@(context.ToUser == null ? context.ToEmail : context.ToUser.DisplayName)</td>
<td>@context.ToDisplayName</td>
<td>@context.Subject</td>
<td>@context.CreatedOn</td>
</Row>
<Detail>
<td colspan="2"></td>
<td colspan="3">@(context.Body.Length > 100 ? context.Body.Substring(0, 100) : context.Body)</td>
<td colspan="3">
@{
string input = "___";
if (context.Body.Contains(input)){
context.Body = context.Body.Split(input)[0];
context.Body = context.Body.Replace("\n", "");
context.Body = context.Body.Replace("\r", "");
} }
@(context.Body.Length > 100 ? (context.Body.Substring(0, 97) + "...") : context.Body)
</td>
</Detail>
</Pager>
}

View File

@ -1,7 +1,7 @@
@namespace Oqtane.Modules.Admin.UserProfile
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IUserRoleService UserRoleService
@inject IUserService UserService
@inject INotificationService NotificationService
@if (PageState.User != null)
@ -9,71 +9,99 @@
<table class="table table-borderless">
<tr>
<td>
<label class="control-label">@title: </label>
</td>
<td>
<select class="form-control" readonly @bind="userid">
<option value="-1">&lt;System&gt;</option>
@if (userroles != null)
{
foreach (UserRole userrole in userroles)
{
<option value="@userrole.UserId">@userrole.User.DisplayName</option>
}
}
</select>
<label class="control-label">@title: </label>
</td>
@if (title == "From")
{
<td>
<input class="form-control" @bind="@username" readonly />
</td>
}
@if (title == "To")
{
<td>
<input class="form-control" @bind="@username" />
</td>
}
</tr>
<tr>
<td>
<label class="control-label">Subject: </label>
</td>
<td>
<input class="form-control" @bind="@subject" />
<label class="control-label">Subject: </label>
</td>
@if (title == "From")
{
<td>
<input class="form-control" @bind="@subject" readonly />
</td>
}
@if (title == "To")
{
<td>
<input class="form-control" @bind="@subject" />
</td>
}
</tr>
@if (title == "From")
{
<tr>
<td>
<label class="control-label">Date: </label>
<label class="control-label">Date: </label>
</td>
<td>
<input class="form-control" @bind="@createdon" />
<input class="form-control" @bind="@createdon" readonly />
</td>
</tr>
}
@if (title == "From")
{
<tr>
<td>
<label class="control-label">Message: </label>
</td>
<td>
<textarea class="form-control" @bind="@body" rows="5" readonly />
</td>
</tr>
}
@if (title == "To")
{
<tr>
<td>
<label class="control-label">Message: </label>
</td>
<td>
<textarea class="form-control" @bind="@body" rows="5" />
</td>
</tr>
}
<tr>
<td>
<label class="control-label">Message: </label>
</td>
<td>
<textarea class="form-control" @bind="@body" rows="5" />
</td>
</tr>
</table>
@if (reply != string.Empty)
{
<button type="button" class="btn btn-primary" @onclick="Send">Send</button>
}
<button type="button" class="btn btn-primary" @onclick="Send">Send</button> }
else
{
if (title == "From")
{
<button type="button" class="btn btn-primary" @onclick="Reply">Reply</button>
}
<button type="button" class="btn btn-primary" @onclick="Reply">Reply</button>}
}
<NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink>
<br />
<br />
<p>@reply</p>
}
@if (title == "To")
{
<div class="control-group">
<label class="control-label">Original Message </label>
<textarea class="form-control" @bind="@reply" rows="5" readonly />
</div>
}
}
@code {
private int notificationid;
private string title = string.Empty;
private List<UserRole> userroles;
private string userid = "-1";
private string username = "";
private string subject = string.Empty;
private string createdon = string.Empty;
private string body = string.Empty;
@ -86,20 +114,17 @@
{
try
{
userroles = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId);
userroles = userroles.Where(item => item.Role.Name == Constants.RegisteredRole || item.Role.Name == Constants.HostRole)
.OrderBy(item => item.User.DisplayName).ToList();
notificationid = Int32.Parse(PageState.QueryString["id"]);
Notification notification = await NotificationService.GetNotificationAsync(notificationid);
if (notification != null)
{
int userid = -1;
if (notification.ToUserId == PageState.User.UserId)
{
title = "From";
if (notification.FromUserId != null)
{
userid = notification.FromUserId.ToString();
userid = notification.FromUserId.Value;
}
}
else
@ -107,10 +132,21 @@
title = "To";
if (notification.ToUserId != null)
{
userid = notification.ToUserId.ToString();
userid = notification.ToUserId.Value;
}
}
if (userid != -1)
{
var user = await UserService.GetUserAsync(userid, PageState.Site.SiteId);
if (user != null)
{
username = user.Username;
}
}
if (username == "")
{
username = "System";
}
subject = notification.Subject;
createdon = notification.CreatedOn.ToString();
body = notification.Body;
@ -126,31 +162,45 @@
private void Reply()
{
title = "To";
subject = "RE: " + subject;
if (!subject.Contains("RE:"))
{
subject = "RE: " + subject;
}
reply = body;
body = "\n\n____________________________________________\nSent: " + createdon + "\nSubject: " + subject + "\n\n" + body;
StateHasChanged();
}
private async Task Send()
{
var notification = new Notification();
notification.SiteId = PageState.Site.SiteId;
notification.FromUserId = PageState.User.UserId;
notification.ToUserId = int.Parse(userid);
notification.ToEmail = string.Empty;
notification.Subject = subject;
notification.Body = body;
notification.ParentId = notificationid;
notification.CreatedOn = DateTime.UtcNow;
notification.IsDelivered = false;
notification.DeliveredOn = null;
try
{
notification = await NotificationService.AddNotificationAsync(notification);
await logger.LogInformation("Notification Created {Notification}", notification);
NavigationManager.NavigateTo(NavigateUrl());
var user = await UserService.GetUserAsync(username, PageState.Site.SiteId);
if (user != null)
{
notification.SiteId = PageState.Site.SiteId;
notification.FromUserId = PageState.User.UserId;
notification.FromDisplayName = PageState.User.DisplayName;
notification.FromEmail = PageState.User.Email;
notification.ToUserId = user.UserId;
notification.ToDisplayName = user.DisplayName;
notification.ToEmail = user.Email;
notification.Subject = subject;
notification.Body = body;
notification.ParentId = notificationid;
notification.CreatedOn = DateTime.UtcNow;
notification.IsDelivered = false;
notification.DeliveredOn = null;
notification.SendOn = DateTime.UtcNow;
notification = await NotificationService.AddNotificationAsync(notification);
await logger.LogInformation("Notification Created {Notification}", notification);
NavigationManager.NavigateTo(NavigateUrl());
}
else
{
AddModuleMessage("User Does Not Exist. Please Verify That The Username Provided Is Correct.", MessageType.Warning);
}
}
catch (Exception ex)
{
@ -158,5 +208,5 @@
AddModuleMessage("Error Adding Notification", MessageType.Error);
}
}
}

View File

@ -2,40 +2,79 @@
@inherits ModuleBase
@inject IUserRoleService UserRoleService
@inject IUserService UserService
@inject ISettingService SettingService
@if (userroles == null)
{
<p><em>Loading...</em></p>
<p>
<em>Loading...</em>
</p>
}
else
{
<ActionLink Action="Add" Text="Add User" />
<ActionLink Action="Add" Text="Add User"/>
<div class="d-flex p-1">
<input class="form-control mr-4" @bind="@_search"/><button class="btn btn-outline-primary ml-1" @onclick="OnSearch">Search</button>
</div>
<Pager Items="@userroles">
<Header>
<th>&nbsp;</th>
<th>&nbsp;</th>
<th>&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>Name</th>
</Header>
<Row>
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.UserId.ToString())" /></td>
<td><ActionDialog Header="Delete User" Message="@("Are You Sure You Wish To Delete " + context.User.DisplayName + "?")" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteUser(context))" /></td>
<td><ActionLink Action="Roles" Parameters="@($"id=" + context.UserId.ToString())" /></td>
<td>
<ActionLink Action="Edit" Parameters="@($"id=" + context.UserId.ToString())"/>
</td>
<td>
<ActionDialog Header="Delete User" Message="@("Are You Sure You Wish To Delete " + context.User.DisplayName + "?")" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteUser(context))"/>
</td>
<td>
<ActionLink Action="Roles" Parameters="@($"id=" + context.UserId.ToString())"/>
</td>
<td>@context.User.DisplayName</td>
</Row>
</Pager>
}
@code {
private List<UserRole> allroles;
private List<UserRole> userroles;
private string _search;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
protected override async Task OnInitializedAsync()
{
userroles = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId);
userroles = userroles.Where(item => item.Role.Name == Constants.RegisteredRole).ToList();
allroles = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId);
await LoadSettingsAsync();
userroles = Search(_search);
}
private List<UserRole> Search(string search)
{
if (string.IsNullOrEmpty(_search))
{
return allroles.Where(item => item.Role.Name == Constants.RegisteredRole).ToList();
}
return allroles
.Where(item => item.Role.Name == Constants.RegisteredRole &&
(
item.User.Username.Contains(search, StringComparison.OrdinalIgnoreCase) ||
item.User.Email.Contains(search, StringComparison.OrdinalIgnoreCase) ||
item.User.DisplayName.Contains(search, StringComparison.OrdinalIgnoreCase)
)
)
.ToList();
}
private async Task OnSearch()
{
userroles = Search(_search);
await UpdateSettingsAsync();
}
private async Task DeleteUser(UserRole UserRole)
@ -56,4 +95,20 @@ else
AddModuleMessage(ex.Message, MessageType.Error);
}
}
private string settingSearch = "AU-search";
private async Task LoadSettingsAsync()
{
Dictionary<string, string> settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId);
_search = SettingService.GetSetting(settings, settingSearch, "");
}
private async Task UpdateSettingsAsync()
{
Dictionary<string, string> settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId);
SettingService.SetSetting(settings, settingSearch, _search);
await SettingService.UpdateUserSettingsAsync(settings, PageState.User.UserId);
}
}

View File

@ -1,6 +1,6 @@
@namespace Oqtane.Modules.Controls
@inherits ModuleBase
@attribute [OqtaneIgnore]
@inherits ModuleControlBase
@if (_visible)
{
<div class="app-admin-modal">
@ -40,7 +40,7 @@
@code {
private bool _visible = false;
private bool _editmode = true;
private bool _editmode = false;
private bool _authorized = false;
private string _iconSpan = string.Empty;
@ -66,7 +66,7 @@
public bool Disabled { get; set; } // optional
[Parameter]
public string EditMode { get; set; } // optional - specifies if a user must be in edit mode to see the action - default is true
public string EditMode { get; set; } // optional - specifies if an authorized user must be in edit mode to see the action - default is false
[Parameter]
public Action OnClick { get; set; } // required if an Action is specified - executes a method in the calling component
@ -84,6 +84,7 @@
{
Class = "btn btn-success";
}
if (!string.IsNullOrEmpty(EditMode))
{
_editmode = bool.Parse(EditMode);

View File

@ -1,6 +1,5 @@
@namespace Oqtane.Modules.Controls
@inherits ModuleBase
@attribute [OqtaneIgnore]
@inherits ModuleControlBase
@inject IUserService UserService
@if (_authorized)
@ -21,7 +20,7 @@
private string _parameters = string.Empty;
private string _classname = "btn btn-primary";
private string _style = string.Empty;
private bool _editmode = true;
private bool _editmode = false;
private bool _authorized = false;
private string _iconSpan = string.Empty;
@ -47,11 +46,14 @@
public bool Disabled { get; set; } // optional
[Parameter]
public string EditMode { get; set; } // optional - specifies if a user must be in edit mode to see the action - default is true
public string EditMode { get; set; } // optional - specifies if an authorized user must be in edit mode to see the action - default is false.
[Parameter]
public string IconName { get; set; } // optional - specifies an icon for the link - default is no icon
[Parameter]
public bool IconOnly { get; set; } // optional - specifies only icon in link
protected override void OnParametersSet()
{
_text = Action;
@ -60,6 +62,11 @@
_text = Text;
}
if (IconOnly && !string.IsNullOrEmpty(IconName))
{
_text = string.Empty;
}
if (!string.IsNullOrEmpty(Parameters))
{
_parameters = Parameters;
@ -82,7 +89,8 @@
if (!string.IsNullOrEmpty(IconName))
{
_iconSpan = $"<span class=\"oi oi-{IconName}\"></span>&nbsp;";
_iconSpan = $"<span class=\"oi oi-{IconName}\"></span>{(IconOnly ? "" : "&nbsp")}";
}
_url = EditUrl(Action, _parameters);

View File

@ -1,6 +1,5 @@
@namespace Oqtane.Modules.Controls
@inherits ModuleBase
@attribute [OqtaneIgnore]
@inherits ModuleControlBase
@if (_text != string.Empty)
{

View File

@ -1,10 +1,7 @@
@namespace Oqtane.Modules.Controls
@inherits ModuleBase
@attribute [OqtaneIgnore]
@inherits ModuleControlBase
@inject IFolderService FolderService
@inject IFileService FileService
@inject IJSRuntime JsRuntime
@if (_folders != null)
{
@ -57,16 +54,16 @@
<div>
@if (UploadMultiple)
{
<input type="file" id="@_fileinputid" name="file" accept="@_filter" multiple/>
<input type="file" id="@_fileinputid" name="file" accept="@_filter" multiple />
}
else
{
<input type="file" id="@_fileinputid" name="file" accept="@_filter"/>
<input type="file" id="@_fileinputid" name="file" accept="@_filter" />
}
<span id="@_progressinfoid"></span><progress id="@_progressbarid" style="width: 150px; visibility: hidden;"></progress>
<span class="float-right">
<button type="button" class="btn btn-success" @onclick="UploadFile">Upload</button>
@if (_showfiles && GetFileId() != -1)
@if (ShowFiles && GetFileId() != -1)
{
<button type="button" class="btn btn-danger" @onclick="DeleteFile">Delete</button>
}
@ -89,7 +86,6 @@
private string _id;
private List<Folder> _folders;
private List<File> _files = new List<File>();
private bool _showfiles = true;
private string _fileinputid = string.Empty;
private string _progressinfoid = string.Empty;
private string _progressbarid = string.Empty;
@ -135,7 +131,7 @@
if (!string.IsNullOrEmpty(Folder))
{
_folders = new List<Folder> {new Folder {FolderId = -1, Name = Folder}};
_folders = new List<Folder> { new Folder { FolderId = -1, Name = Folder } };
FolderId = -1;
}
else
@ -164,7 +160,7 @@
await GetFiles();
// create unique id for component
// create unique id for component
_guid = Guid.NewGuid().ToString("N");
_fileinputid = _guid + "FileInput";
_progressinfoid = _guid + "ProgressInfo";
@ -212,7 +208,7 @@
_message = string.Empty;
try
{
FolderId = int.Parse((string) e.Value);
FolderId = int.Parse((string)e.Value);
await GetFiles();
FileId = -1;
_image = string.Empty;
@ -228,7 +224,7 @@
private async Task FileChanged(ChangeEventArgs e)
{
_message = string.Empty;
FileId = int.Parse((string) e.Value);
FileId = int.Parse((string)e.Value);
await SetImage();
StateHasChanged();
@ -245,8 +241,8 @@
var maxwidth = 200;
var maxheight = 200;
var ratioX = (double) maxwidth / (double) file.ImageWidth;
var ratioY = (double) maxheight / (double) file.ImageHeight;
var ratioX = (double)maxwidth / (double)file.ImageWidth;
var ratioY = (double)maxheight / (double)file.ImageHeight;
var ratio = ratioX < ratioY ? ratioX : ratioY;
_image = "<img src=\"" + ContentUrl(FileId) + "\" alt=\"" + file.Name +
@ -258,7 +254,7 @@
private async Task UploadFile()
{
var interop = new Interop(JsRuntime);
var interop = new Interop(JSRuntime);
var upload = await interop.GetFiles(_fileinputid);
if (upload.Length > 0)
{

View File

@ -1,6 +1,5 @@
@namespace Oqtane.Modules.Controls
@inherits ModuleBase
@attribute [OqtaneIgnore]
@inherits ModuleControlBase
@if (!string.IsNullOrEmpty(HelpText))
{

View File

@ -1,10 +1,16 @@
@namespace Oqtane.Modules.Controls
@inherits ModuleBase
@attribute [OqtaneIgnore]
@inherits ModuleControlBase
@inject NavigationManager NavigationManager
@if (!string.IsNullOrEmpty(_message))
{
<div class="@_classname" role="alert">@_message</div>
<div class="@_classname" role="alert">
@_message
@if (Type == MessageType.Error && UserSecurity.IsAuthorized(PageState.User, Constants.HostRole))
{
@((MarkupString)"&nbsp;&nbsp;")<NavLink href="@NavigateUrl("admin/log")">View Details</NavLink>
}
</div>
<br />
}

View File

@ -1,41 +1,39 @@
@namespace Oqtane.Modules.Controls
@inherits ModuleBase
@attribute [OqtaneIgnore]
@inherits ModuleControlBase
@typeparam TableItem
<p>
@if(Format == "Table")
@if (Format == "Table")
{
<table class="@Class">
<thead>
<tr>@Header</tr>
</thead>
<tbody>
<table class="@Class">
<thead>
<tr>@Header</tr>
</thead>
<tbody>
@foreach (var item in ItemList)
{
<tr>@Row(item)</tr>
@if (Detail != null)
{
<tr>@Detail(item)</tr>
}
}
</tbody>
</table>
}
@if (Format == "Grid")
{
<div class="@Class">
<div class="row">@Header</div>
@foreach (var item in ItemList)
{
<tr>@Row(item)</tr>
<div class="row">@Row(item)</div>
@if (Detail != null)
{
<tr>@Detail(item)</tr>
<div class="row">@Detail(item)</div>
}
}
</tbody>
</table>
}
@if(Format == "Grid")
{
<div class="@Class">
<div class="row">@Header</div>
@foreach (var item in ItemList)
{
<div class="row">@Row(item)</div>
@if (Detail != null)
{
<div class="row">@Detail(item)</div>
}
}
</div>
</div>
}
<div class="mx-auto text-center">
@if (_page > _maxPages)

View File

@ -1,6 +1,5 @@
@namespace Oqtane.Modules.Controls
@inherits ModuleBase
@attribute [OqtaneIgnore]
@inherits ModuleControlBase
@inject IRoleService RoleService
@inject IUserService UserService
@ -10,10 +9,10 @@
<table class="table" style="width: 50%; min-width: 250px;">
<tbody>
<tr>
<th>Role</th>
<th scope="col">Role</th>
@foreach (PermissionString permission in _permissions)
{
<th style="text-align: center;">@permission.PermissionName</th>
<th style="text-align: center; width: 1px;">@permission.PermissionName</th>
}
</tr>
@foreach (Role role in _roles)
@ -36,10 +35,10 @@
<table class="table" style="width: 50%; min-width: 250px;">
<thead>
<tr>
<th>User</th>
<th scope="col">User</th>
@foreach (PermissionString permission in _permissions)
{
<th style="text-align: center;">@permission.PermissionName</th>
<th style="text-align: center; width: 1px;">@permission.PermissionName</th>
}
</tr>
</thead>
@ -52,7 +51,7 @@
@foreach (PermissionString permission in _permissions)
{
var p = permission;
<td style="text-align: center;">
<td style="text-align: center; width: 1px;">
<TriStateCheckBox Value=@GetPermissionValue(p.Permissions, userid) Disabled=false OnChange="@(e => PermissionChanged(e, p.PermissionName, userid))" />
</td>
}
@ -64,9 +63,10 @@
<table class="table" style="width: 50%; min-width: 250px;">
<tbody>
<tr>
<td style="text-align: right;"><label for="Username" class="control-label">User: </label></td>
<td><input type="text" name="Username" class="form-control" placeholder="Enter Username" @bind="@_username" /></td>
<td style="text-align: left;"><button type="button" class="btn btn-primary" @onclick="AddUser">Add</button></td>
<td class="input-group">
<input type="text" name="Username" class="form-control" placeholder="Enter Username" @bind="@_username" />
<button type="button" class="btn btn-primary" @onclick="AddUser">Add</button>
</td>
</tr>
</tbody>
</table>

View File

@ -1,7 +1,5 @@
@namespace Oqtane.Modules.Controls
@inherits ModuleBase
@attribute [OqtaneIgnore]
@inject IJSRuntime JsRuntime
@inherits ModuleControlBase
<div class="row" style="margin-bottom: 50px;">
<div class="col">
@ -108,6 +106,13 @@
[Parameter]
public string DebugLevel { get; set; } = "info";
public override List<Resource> Resources => new List<Resource>()
{
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill1.3.6.min.js" },
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill-blot-formatter.min.js" },
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill-interop.js" }
};
protected override void OnInitialized()
{
_content = Content; // raw HTML
@ -117,8 +122,11 @@
{
if (firstRender)
{
await RichTextEditorInterop.CreateEditor(
JsRuntime,
await base.OnAfterRenderAsync(firstRender);
var interop = new RichTextEditorInterop(JSRuntime);
await interop.CreateEditor(
_editorElement,
_toolBar,
ReadOnly,
@ -126,14 +134,10 @@
Theme,
DebugLevel);
await RichTextEditorInterop.LoadEditorContent(
JsRuntime,
_editorElement, Content);
await interop.LoadEditorContent(_editorElement, Content);
// preserve a copy of the rich text content ( Quill sanitizes content so we need to retrieve it from the editor )
_original = await RichTextEditorInterop.GetHtml(
JsRuntime,
_editorElement);
_original = await interop.GetHtml(_editorElement);
}
}
@ -146,25 +150,22 @@
public async Task RefreshRichText()
{
await RichTextEditorInterop.LoadEditorContent(
JsRuntime,
_editorElement, _content);
var interop = new RichTextEditorInterop(JSRuntime);
await interop.LoadEditorContent(_editorElement, _content);
}
public async Task RefreshRawHtml()
{
_content = await RichTextEditorInterop.GetHtml(
JsRuntime,
_editorElement);
var interop = new RichTextEditorInterop(JSRuntime);
_content = await interop.GetHtml(_editorElement);
StateHasChanged();
}
public async Task<string> GetHtml()
{
// get rich text content
string content = await RichTextEditorInterop.GetHtml(
JsRuntime,
_editorElement);
var interop = new RichTextEditorInterop(JSRuntime);
string content = await interop.GetHtml(_editorElement);
if (_original != content)
{
@ -185,9 +186,8 @@
var fileid = _fileManager.GetFileId();
if (fileid != -1)
{
await RichTextEditorInterop.InsertImage(
JsRuntime,
_editorElement, ContentUrl(fileid));
var interop = new RichTextEditorInterop(JSRuntime);
await interop.InsertImage(_editorElement, ContentUrl(fileid));
_filemanagervisible = false;
_message = string.Empty;
}
@ -207,22 +207,19 @@
// other rich text editor methods which can be used by developers
public async Task<string> GetText()
{
return await RichTextEditorInterop.GetText(
JsRuntime,
_editorElement);
var interop = new RichTextEditorInterop(JSRuntime);
return await interop.GetText(_editorElement);
}
public async Task<string> GetContent()
{
return await RichTextEditorInterop.GetContent(
JsRuntime,
_editorElement);
var interop = new RichTextEditorInterop(JSRuntime);
return await interop.GetContent(_editorElement);
}
public async Task EnableEditor(bool mode)
{
await RichTextEditorInterop.EnableEditor(
JsRuntime,
_editorElement, mode);
var interop = new RichTextEditorInterop(JSRuntime);
await interop.EnableEditor(_editorElement, mode);
}
}

View File

@ -4,10 +4,16 @@ using System.Threading.Tasks;
namespace Oqtane.Modules.Controls
{
public static class RichTextEditorInterop
public class RichTextEditorInterop
{
internal static ValueTask<object> CreateEditor(
IJSRuntime jsRuntime,
private readonly IJSRuntime _jsRuntime;
public RichTextEditorInterop(IJSRuntime jsRuntime)
{
_jsRuntime = jsRuntime;
}
public async Task CreateEditor(
ElementReference quillElement,
ElementReference toolbar,
bool readOnly,
@ -15,66 +21,103 @@ namespace Oqtane.Modules.Controls
string theme,
string debugLevel)
{
return jsRuntime.InvokeAsync<object>(
"interop.createQuill",
quillElement, toolbar, readOnly,
placeholder, theme, debugLevel);
try
{
await _jsRuntime.InvokeAsync<object>(
"Oqtane.RichTextEditor.createQuill",
quillElement, toolbar, readOnly, placeholder, theme, debugLevel);
return;
}
catch
{
// handle exception
}
}
internal static ValueTask<string> GetText(
IJSRuntime jsRuntime,
ElementReference quillElement)
public ValueTask<string> GetText(ElementReference quillElement)
{
return jsRuntime.InvokeAsync<string>(
"interop.getQuillText",
quillElement);
try
{
return _jsRuntime.InvokeAsync<string>(
"Oqtane.RichTextEditor.getQuillText",
quillElement);
}
catch
{
return new ValueTask<string>(Task.FromResult(string.Empty));
}
}
internal static ValueTask<string> GetHtml(
IJSRuntime jsRuntime,
ElementReference quillElement)
public ValueTask<string> GetHtml(ElementReference quillElement)
{
return jsRuntime.InvokeAsync<string>(
"interop.getQuillHTML",
quillElement);
try
{
return _jsRuntime.InvokeAsync<string>(
"Oqtane.RichTextEditor.getQuillHTML",
quillElement);
}
catch
{
return new ValueTask<string>(Task.FromResult(string.Empty));
}
}
internal static ValueTask<string> GetContent(
IJSRuntime jsRuntime,
ElementReference quillElement)
public ValueTask<string> GetContent(ElementReference quillElement)
{
return jsRuntime.InvokeAsync<string>(
"interop.getQuillContent",
quillElement);
try
{
return _jsRuntime.InvokeAsync<string>(
"Oqtane.RichTextEditor.getQuillContent",
quillElement);
}
catch
{
return new ValueTask<string>(Task.FromResult(string.Empty));
}
}
internal static ValueTask<object> LoadEditorContent(
IJSRuntime jsRuntime,
ElementReference quillElement,
string content)
public Task LoadEditorContent(ElementReference quillElement, string content)
{
return jsRuntime.InvokeAsync<object>(
"interop.loadQuillContent",
quillElement, content);
try
{
_jsRuntime.InvokeAsync<object>(
"Oqtane.RichTextEditor.loadQuillContent",
quillElement, content);
return Task.CompletedTask;
}
catch
{
return Task.CompletedTask;
}
}
internal static ValueTask<object> EnableEditor(
IJSRuntime jsRuntime,
ElementReference quillElement,
bool mode)
public Task EnableEditor(ElementReference quillElement, bool mode)
{
return jsRuntime.InvokeAsync<object>(
"interop.enableQuillEditor", quillElement, mode);
try
{
_jsRuntime.InvokeAsync<object>(
"Oqtane.RichTextEditor.enableQuillEditor", quillElement, mode);
return Task.CompletedTask;
}
catch
{
return Task.CompletedTask;
}
}
internal static ValueTask<object> InsertImage(
IJSRuntime jsRuntime,
ElementReference quillElement,
string imageUrl)
public Task InsertImage(ElementReference quillElement, string imageUrl)
{
return jsRuntime.InvokeAsync<object>(
"interop.insertQuillImage",
quillElement, imageUrl);
try
{
_jsRuntime.InvokeAsync<object>(
"Oqtane.RichTextEditor.insertQuillImage",
quillElement, imageUrl);
return Task.CompletedTask;
}
catch
{
return Task.CompletedTask;
}
}
}
}

View File

@ -1,15 +1,14 @@
@namespace Oqtane.Modules.Controls
@inherits ModuleBase
@attribute [OqtaneIgnore]
@inherits ModuleControlBase
<div class="d-flex">
<div>
<a data-toggle="collapse" class="app-link-unstyled" href="#@Name" aria-expanded="@_expanded" aria-controls="@Name">
<a data-toggle="collapse" class="app-link-unstyled" href="#@Name" aria-expanded="@_expanded" aria-controls="@Name" @onclick:preventDefault="true">
<h5>@_heading</h5>
</a>
</div>
<div class="ml-auto">
<a data-toggle="collapse" class="app-link-unstyled float-right" href="#@Name" aria-expanded="@_expanded" aria-controls="@Name">
<a data-toggle="collapse" class="app-link-unstyled float-right" href="#@Name" aria-expanded="@_expanded" aria-controls="@Name" @onclick:preventDefault="true">
<i class="oi oi-chevron-bottom"></i>&nbsp;
</a>
</div>

View File

@ -1,6 +1,5 @@
@namespace Oqtane.Modules.Controls
@inherits ModuleBase
@attribute [OqtaneIgnore]
@inherits ModuleControlBase
@if (Name == Parent.ActiveTab)
{

View File

@ -1,6 +1,5 @@
@namespace Oqtane.Modules.Controls
@inherits ModuleBase
@attribute [OqtaneIgnore]
@inherits ModuleControlBase
<CascadingValue Value="this">
<div class="container-fluid">
@ -11,13 +10,13 @@
<li class="nav-item">
@if (tabPanel.Name == ActiveTab)
{
<a class="nav-link active" data-toggle="tab" href="#@tabPanel.Name" role="tab">
<a class="nav-link active" data-toggle="tab" href="#@tabPanel.Name" role="tab" @onclick:preventDefault="true">
@DisplayHeading(tabPanel.Name, tabPanel.Heading)
</a>
}
else
{
<a class="nav-link" data-toggle="tab" href="#@tabPanel.Name" role="tab">
<a class="nav-link" data-toggle="tab" href="#@tabPanel.Name" role="tab" @onclick:preventDefault="true">
@DisplayHeading(tabPanel.Name, tabPanel.Heading)
</a>
}

View File

@ -1,4 +1,5 @@
@namespace Oqtane.Modules.Controls
@inherits ModuleControlBase
<img src="@_src" title="@_title" @onclick="SetValue" />

View File

@ -27,12 +27,8 @@
public override List<Resource> Resources => new List<Resource>()
{
new Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css" },
// the following resources should be declared in the RichTextEditor component however the framework currently only supports resource management for modules and themes
new Resource { ResourceType = ResourceType.Stylesheet, Url = "css/quill/quill1.3.6.bubble.css" },
new Resource { ResourceType = ResourceType.Stylesheet, Url = "css/quill/quill1.3.6.snow.css" },
new Resource { ResourceType = ResourceType.Script, Url = "js/quill1.3.6.min.js" },
new Resource { ResourceType = ResourceType.Script, Url = "js/quill-blot-formatter.min.js" },
new Resource { ResourceType = ResourceType.Script, Url = "js/quill-interop.js" }
new Resource { ResourceType = ResourceType.Stylesheet, Url = "css/quill/quill1.3.6.snow.css" }
};
private RichTextEditor RichTextEditorHtml;

View File

@ -7,7 +7,7 @@
@if (PageState.EditMode)
{
<br /><ActionLink Action="Edit" /><br /><br />
<br /><ActionLink Action="Edit" EditMode="true" /><br /><br />
}
@code {

View File

@ -21,23 +21,23 @@ namespace Oqtane.Modules.HtmlText.Services
public async Task<HtmlTextInfo> GetHtmlTextAsync(int moduleId)
{
var htmltext = await GetJsonAsync<List<HtmlTextInfo>>($"{ApiUrl}/{moduleId}?entityid={moduleId}");
var htmltext = await GetJsonAsync<List<HtmlTextInfo>>(CreateAuthorizationPolicyUrl($"{ApiUrl}/{moduleId}", moduleId));
return htmltext.FirstOrDefault();
}
public async Task AddHtmlTextAsync(HtmlTextInfo htmlText)
{
await PostJsonAsync($"{ApiUrl}?entityid={htmlText.ModuleId}", htmlText);
await PostJsonAsync(CreateAuthorizationPolicyUrl($"{ApiUrl}", htmlText.ModuleId), htmlText);
}
public async Task UpdateHtmlTextAsync(HtmlTextInfo htmlText)
{
await PutJsonAsync($"{ApiUrl}/{htmlText.HtmlTextId}?entityid={htmlText.ModuleId}", htmlText);
await PutJsonAsync(CreateAuthorizationPolicyUrl($"{ApiUrl}/{htmlText.HtmlTextId}", htmlText.ModuleId), htmlText);
}
public async Task DeleteHtmlTextAsync(int moduleId)
{
await DeleteAsync($"{ApiUrl}/{moduleId}?entityid={moduleId}");
await DeleteAsync(CreateAuthorizationPolicyUrl($"{ApiUrl}/{moduleId}", moduleId));
}
}
}

View File

@ -7,6 +7,8 @@ using System;
using Oqtane.Enums;
using Oqtane.UI;
using System.Collections.Generic;
using Microsoft.JSInterop;
using System.Linq;
namespace Oqtane.Modules
{
@ -19,6 +21,9 @@ namespace Oqtane.Modules
[Inject]
protected ILogService LoggingService { get; set; }
[Inject]
protected IJSRuntime JSRuntime { get; set; }
[CascadingParameter]
protected PageState PageState { get; set; }
@ -28,7 +33,6 @@ namespace Oqtane.Modules
[CascadingParameter]
protected ModuleInstance ModuleInstance { get; set; }
// optional interface properties
public virtual SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.View; } set { } } // default security
@ -40,6 +44,24 @@ namespace Oqtane.Modules
public virtual List<Resource> Resources { get; set; }
// base lifecycle method for handling JSInterop script registration
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
if (Resources != null && Resources.Exists(item => item.ResourceType == ResourceType.Script))
{
var scripts = new List<object>();
foreach (Resource resource in Resources.Where(item => item.ResourceType == ResourceType.Script))
{
scripts.Add(new { href = resource.Url, bundle = resource.Bundle ?? "", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "" });
}
var interop = new Interop(JSRuntime);
await interop.IncludeScripts(scripts.ToArray());
}
}
}
// path method
@ -94,6 +116,54 @@ namespace Oqtane.Modules
return Utilities.ContentUrl(PageState.Alias, fileid);
}
public virtual Dictionary<string, string> GetUrlParameters(string parametersTemplate = "")
{
var urlParameters = new Dictionary<string, string>();
string[] templateSegments;
var parameters = PageState.UrlParameters.Split('/', StringSplitOptions.RemoveEmptyEntries);
var parameterId = 0;
if (string.IsNullOrEmpty(parametersTemplate))
{
for (int i = 0; i < parameters.Length; i++)
{
urlParameters.TryAdd("parameter" + i, parameters[i]);
}
}
else
{
templateSegments = parametersTemplate.Split('/', StringSplitOptions.RemoveEmptyEntries);
if (parameters.Length == templateSegments.Length)
{
for (int i = 0; i < parameters.Length; i++)
{
if (parameters.Length > i)
{
if (templateSegments[i] == parameters[i])
{
urlParameters.TryAdd("parameter" + parameterId, parameters[i]);
parameterId++;
}
else if (templateSegments[i].StartsWith("{") && templateSegments[i].EndsWith("}"))
{
var key = templateSegments[i].Replace("{", "");
key = key.Replace("}", "");
urlParameters.TryAdd(key, parameters[i]);
}
else
{
i = parameters.Length;
urlParameters.Clear();
}
}
}
}
}
return urlParameters;
}
// user feedback methods
public void AddModuleMessage(string message, MessageType type)
{
@ -132,12 +202,15 @@ namespace Oqtane.Modules
case "add":
logFunction = LogFunction.Create;
break;
case "edit":
logFunction = LogFunction.Update;
break;
case "delete":
logFunction = LogFunction.Delete;
break;
default:
logFunction = LogFunction.Read;
break;

View File

@ -0,0 +1,10 @@
using Oqtane.Shared;
namespace Oqtane.Modules
{
[OqtaneIgnore]
public abstract class ModuleControlBase : ModuleBase
{
}
}

View File

@ -6,7 +6,7 @@
<LangVersion>7.3</LangVersion>
<RazorLangVersion>3.0</RazorLangVersion>
<Configurations>Debug;Release</Configurations>
<Version>1.0.0</Version>
<Version>1.0.3</Version>
<Product>Oqtane</Product>
<Authors>Shaun Walker</Authors>
<Company>.NET Foundation</Company>
@ -15,8 +15,9 @@
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
<RepositoryUrl>https://github.com/oqtane</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
<PackageReleaseNotes>Not for production use.</PackageReleaseNotes>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v1.0.3</PackageReleaseNotes>
<RootNamespace>Oqtane</RootNamespace>
<IsPackable>true</IsPackable>
</PropertyGroup>
<ItemGroup>
@ -37,5 +38,4 @@
<ItemGroup>
<ProjectReference Include="..\Oqtane.Shared\Oqtane.Shared.csproj" />
</ItemGroup>
</Project>

View File

@ -7,9 +7,9 @@ namespace Oqtane.Services
public interface IThemeService
{
Task<List<Theme>> GetThemesAsync();
Dictionary<string, string> GetThemeTypes(List<Theme> themes);
Dictionary<string, string> GetPaneLayoutTypes(List<Theme> themes, string themeName);
Dictionary<string, string> GetContainerTypes(List<Theme> themes);
List<ThemeControl> GetThemeControls(List<Theme> themes);
List<ThemeControl> GetLayoutControls(List<Theme> themes, string themeName);
List<ThemeControl> GetContainerControls(List<Theme> themes, string themeName);
Task InstallThemesAsync();
Task DeleteThemeAsync(string themeName);
}

View File

@ -170,6 +170,19 @@ namespace Oqtane.Services
// can be used to override the default alias
public Alias Alias { get; set; }
// add entityid parameter to url for custom authorization policy
public string CreateAuthorizationPolicyUrl(string url, int entityId)
{
if (url.Contains("?"))
{
return url + "&entityid=" + entityId.ToString();
}
else
{
return url + "?entityid=" + entityId.ToString();
}
}
[Obsolete("This method is obsolete. Use CreateApiUrl(Alias alias, string serviceName) instead.", false)]
public string CreateApiUrl(Alias alias, string absoluteUri, string serviceName)
{

View File

@ -153,7 +153,7 @@ namespace Oqtane.Services
public string GetSetting(Dictionary<string, string> settings, string settingName, string defaultValue)
{
string value = defaultValue;
if (settings.ContainsKey(settingName))
if (settings != null && settings.ContainsKey(settingName))
{
value = settings[settingName];
}
@ -162,6 +162,10 @@ namespace Oqtane.Services
public Dictionary<string, string> SetSetting(Dictionary<string, string> settings, string settingName, string settingValue)
{
if (settings == null)
{
settings = new Dictionary<string, string>();
}
if (settings.ContainsKey(settingName))
{
settings[settingName] = settingValue;

View File

@ -1,81 +1,54 @@
using Oqtane.Models;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using System.Reflection;
using System;
using Oqtane.Models;
using Oqtane.Shared;
namespace Oqtane.Services
{
public class ThemeService : ServiceBase, IThemeService
{
private readonly HttpClient _http;
private readonly SiteState _siteState;
public ThemeService(HttpClient http) : base(http)
public ThemeService(HttpClient http, SiteState siteState) : base(http)
{
_http = http;
_siteState = siteState;
}
private string Apiurl => CreateApiUrl("Theme");
private string ApiUrl => CreateApiUrl(_siteState.Alias, "Theme");
public async Task<List<Theme>> GetThemesAsync()
{
List<Theme> themes = await GetJsonAsync<List<Theme>>(Apiurl);
List<Theme> themes = await GetJsonAsync<List<Theme>>(ApiUrl);
return themes.OrderBy(item => item.Name).ToList();
}
public Dictionary<string, string> GetThemeTypes(List<Theme> themes)
public List<ThemeControl> GetThemeControls(List<Theme> themes)
{
var selectableThemes = new Dictionary<string, string>();
foreach (Theme theme in themes)
{
foreach (string themecontrol in theme.ThemeControls.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries))
{
selectableThemes.Add(themecontrol, theme.Name + " - " + Utilities.GetTypeNameLastSegment(themecontrol, 0));
}
}
return selectableThemes;
return themes.SelectMany(item => item.Themes).ToList();
}
public Dictionary<string, string> GetPaneLayoutTypes(List<Theme> themes, string themeName)
public List<ThemeControl> GetLayoutControls(List<Theme> themes, string themeName)
{
var selectablePaneLayouts = new Dictionary<string, string>();
foreach (Theme theme in themes)
{
if (themeName.StartsWith(theme.ThemeName))
{
foreach (string panelayout in theme.PaneLayouts.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries))
{
selectablePaneLayouts.Add(panelayout, theme.Name + " - " + @Utilities.GetTypeNameLastSegment(panelayout, 0));
}
}
}
return selectablePaneLayouts;
return themes.Where(item => Utilities.GetTypeName(themeName).StartsWith(Utilities.GetTypeName(item.ThemeName)))
.SelectMany(item => item.Layouts).ToList();
}
public Dictionary<string, string> GetContainerTypes(List<Theme> themes)
public List<ThemeControl> GetContainerControls(List<Theme> themes, string themeName)
{
var selectableContainers = new Dictionary<string, string>();
foreach (Theme theme in themes)
{
foreach (string container in theme.ContainerControls.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries))
{
selectableContainers.Add(container, theme.Name + " - " + @Utilities.GetTypeNameLastSegment(container, 0));
}
}
return selectableContainers;
return themes.Where(item => Utilities.GetTypeName(themeName).StartsWith(Utilities.GetTypeName(item.ThemeName)))
.SelectMany(item => item.Containers).ToList();
}
public async Task InstallThemesAsync()
{
await GetJsonAsync<List<string>>($"{Apiurl}/install");
await GetJsonAsync<List<string>>($"{ApiUrl}/install");
}
public async Task DeleteThemeAsync(string themeName)
{
await DeleteAsync($"{Apiurl}/{themeName}");
await DeleteAsync($"{ApiUrl}/{themeName}");
}
}
}

View File

@ -30,6 +30,11 @@
public override List<Resource> Resources => new List<Resource>()
{
new Resource { ResourceType = ResourceType.Stylesheet, Url = ThemePath() + "Theme.css" }
new Resource { ResourceType = ResourceType.Stylesheet, Url = "https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css", Integrity = "sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk", CrossOrigin = "anonymous" },
new Resource { ResourceType = ResourceType.Stylesheet, Url = ThemePath() + "Theme.css" },
new Resource { ResourceType = ResourceType.Script, Bundle = "Bootstrap", Url = "https://code.jquery.com/jquery-3.5.1.slim.min.js", Integrity = "sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj", CrossOrigin = "anonymous" },
new Resource { ResourceType = ResourceType.Script, Bundle = "Bootstrap", Url = "https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js", Integrity = "sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo", CrossOrigin = "anonymous" },
new Resource { ResourceType = ResourceType.Script, Bundle = "Bootstrap", Url = "https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js", Integrity = "sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI", CrossOrigin = "anonymous" }
};
}

View File

@ -1,62 +1,12 @@
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using Oqtane.Shared;
using Oqtane.Models;
using Oqtane.UI;
namespace Oqtane.Themes
{
public class ContainerBase : ComponentBase, IContainerControl
public abstract class ContainerBase : ThemeBase, IContainerControl
{
[Inject]
protected IJSRuntime JSRuntime { get; set; }
[CascadingParameter]
protected PageState PageState { get; set; }
[CascadingParameter]
protected Module ModuleState { get; set; }
public string ThemePath()
{
return "Themes/" + GetType().Namespace + "/";
}
public string NavigateUrl()
{
return NavigateUrl(PageState.Page.Path);
}
public string NavigateUrl(string path)
{
return NavigateUrl(path, "");
}
public string NavigateUrl(string path, string parameters)
{
return Utilities.NavigateUrl(PageState.Alias.Path, path, parameters);
}
public string EditUrl(string action, string parameters)
{
return EditUrl(ModuleState.ModuleId, action, parameters);
}
public string EditUrl(int moduleid, string action)
{
return EditUrl(moduleid, action, "");
}
public string EditUrl(int moduleid, string action, string parameters)
{
return EditUrl(PageState.Page.Path, moduleid, action, parameters);
}
public string EditUrl(string path, int moduleid, string action, string parameters)
{
return Utilities.EditUrl(PageState.Alias.Path, path, moduleid, action, parameters);
}
}
}

View File

@ -1,6 +1,5 @@
@namespace Oqtane.Themes.Controls
@inherits ThemeControlBase
@attribute [OqtaneIgnore]
@if (BreadCrumbPages.Any())
{

View File

@ -1,6 +1,5 @@
@namespace Oqtane.Themes.Controls
@inherits ThemeControlBase
@attribute [OqtaneIgnore]
@inject NavigationManager NavigationManager
@inject IUserService UserService
@inject IModuleDefinitionService ModuleDefinitionService
@ -9,8 +8,9 @@
@inject IPageService PageService
@inject IPageModuleService PageModuleService
@inject ILogService logger
@inject ISettingService SettingService
@if (_moduleDefinitions != null && UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, PageState.Page.Permissions))
@if (_moduleDefinitions != null && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions))
{
<div class="app-controlpanel" style="@_display">
@ -49,6 +49,21 @@
<button class="btn btn-danger btn-block mx-auto" @onclick="ConfirmDelete">Delete</button>
</div>
</div>
<br />
<div class="row">
@if (UserSecurity.GetPermissionStrings(PageState.Page.Permissions).FirstOrDefault(item => item.PermissionName == PermissionNames.View).Permissions.Split(';').Contains(Constants.AllUsersRole))
{
<div class="col">
<button type="button" class="btn btn-primary btn-block mx-auto" @onclick=@(async () => Publish("unpublish"))>Unpublish Page</button>
</div>
}
else
{
<div class="col">
<button type="button" class="btn btn-primary btn-block mx-auto" @onclick=@(async () => Publish("publish"))>Publish Page</button>
</div>
}
</div>
}
@if (_deleteConfirmation)
@ -78,18 +93,18 @@
<div class="row">
<div class="col text-center">
<label for="Module" class="control-label">Module Management: </label>
<select class="form-control" @bind="@_moduleType">
<select class="form-control" @bind="@ModuleType">
<option value="new">Add New Module</option>
<option value="existing">Add Existing Module</option>
</select>
@if (_moduleType == "new")
@if (ModuleType == "new")
{
@if (_moduleDefinitions != null)
{
<select class="form-control" @onchange="(e => CategoryChanged(e))">
@foreach (var category in _categories)
{
if (category == _category)
if (category == Category)
{
<option value="@category" selected>@category Modules</option>
}
@ -100,7 +115,7 @@
}
</select>
<select class="form-control" @onchange="(e => ModuleChanged(e))">
@if (_moduleDefinitionName == "-")
@if (ModuleDefinitionName == "-")
{
<option value="-" selected>&lt;Select Module&gt;</option>
}
@ -112,11 +127,14 @@
{
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Utilize, moduledefinition.Permissions))
{
<option value="@moduledefinition.ModuleDefinitionName">@moduledefinition.Name</option>
if (moduledefinition.Runtimes == "" || moduledefinition.Runtimes.Contains(PageState.Runtime.ToString()))
{
<option value="@moduledefinition.ModuleDefinitionName">@moduledefinition.Name</option>
}
}
}
</select>
@((MarkupString)@_description)
@((MarkupString) Description)
}
}
else
@ -128,7 +146,7 @@
<option value="@p.PageId">@p.Name</option>
}
</select>
<select class="form-control" @bind="@_moduleId">
<select class="form-control" @bind="@ModuleId">
<option value="-">&lt;Select Module&gt;</option>
@foreach (Module module in _modules)
{
@ -141,28 +159,30 @@
<div class="row">
<div class="col text-center">
<label for="Title" class="control-label">Title: </label>
<input type="text" name="Title" class="form-control" @bind="@_title" />
<input type="text" name="Title" class="form-control" @bind="@Title" />
</div>
</div>
<div class="row">
<div class="col text-center">
<label for="Pane" class="control-label">Pane: </label>
<select class="form-control" @bind="@_pane">
<option value="">&lt;Select Pane&gt;</option>
@foreach (string pane in PageState.Page.Panes)
{
<option value="@pane">@pane Pane</option>
}
</select>
@if (_pane.Length > 1)
{
<div class="row">
<div class="col text-center">
<label for="Pane" class="control-label">Pane: </label>
<select class="form-control" @bind="@Pane">
@foreach (string pane in PageState.Page.Panes)
{
<option value="@pane">@pane Pane</option>
}
</select>
</div>
</div>
</div>
}
<div class="row">
<div class="col text-center">
<label for="Container" class="control-label">Container: </label>
<select class="form-control" @bind="@_containerType">
@foreach (KeyValuePair<string, string> container in _containers)
<select class="form-control" @bind="@ContainerType">
@foreach (var container in _containers)
{
<option value="@container.Key">@container.Value</option>
<option value="@container.TypeName">@container.Name</option>
}
</select>
</div>
@ -171,223 +191,201 @@
<br />
<button type="button" class="btn btn-primary btn-block mx-auto" @onclick="@AddModule">Add Module To Page</button>
@((MarkupString) _message)
@((MarkupString) Message)
</div>
</div>
</div>
}
@if (UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, PageState.Page.Permissions) || (PageState.Page.IsPersonalizable && PageState.User != null))
@if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions) || (PageState.Page.IsPersonalizable && PageState.User != null))
{
@if (PageState.Page.EditMode)
if (PageState.EditMode)
{
<button type="button" class="btn @ButtonClass active" aria-pressed="true" autocomplete="off">
<button type="button" class="btn @ButtonClass active" data-toggle="button" aria-pressed="true" autocomplete="off" @onclick="(async () => await ToggleEditMode(PageState.EditMode))">
<span class="oi oi-pencil"></span>
</button>
}
else
{
if (PageState.EditMode)
{
<button type="button" class="btn @ButtonClass active" data-toggle="button" aria-pressed="true" autocomplete="off" @onclick="(async () => await ToggleEditMode(PageState.EditMode))">
<span class="oi oi-pencil"></span>
</button>
}
else
{
<button type="button" class="btn @ButtonClass" data-toggle="button" aria-pressed="false" autocomplete="off" @onclick="(async () => await ToggleEditMode(PageState.EditMode))">
<span class="oi oi-pencil"></span>
</button>
}
<button type="button" class="btn @ButtonClass" data-toggle="button" aria-pressed="false" autocomplete="off" @onclick="(async () => await ToggleEditMode(PageState.EditMode))">
<span class="oi oi-pencil"></span>
</button>
}
}
@if (UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, PageState.Page.Permissions))
@if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions))
{
<button type="button" class="btn @ButtonClass" @onclick="ShowControlPanel">
<span class="oi oi-cog"></span>
</button>
}
@code {
@code{
private bool _deleteConfirmation = false;
private string _moduleType = "new";
private List<string> _categories = new List<string>();
private List<ModuleDefinition> _allModuleDefinitions;
private List<ModuleDefinition> _moduleDefinitions;
private List<Page> _pages = new List<Page>();
private string _pageId = "-";
private string _moduleId = "-";
private List<Module> _modules = new List<Module>();
private Dictionary<string, string> _containers = new Dictionary<string, string>();
private string _moduleDefinitionName = "-";
private string _category = "Common";
private string _description = "";
private string _pane = "";
private string _title = "";
private string _containerType = "";
private List<ThemeControl> _containers = new List<ThemeControl>();
private string _display = "display: none;";
private string _message = "";
private string _category = "Common";
[Parameter]
public string ButtonClass { get; set; }
protected string PageId { get; private set; } = "-";
protected string ModuleId { get; private set; } = "-";
protected string ModuleType { get; private set; } = "new";
protected string ModuleDefinitionName { get; private set; } = "-";
[Parameter]
public string CardClass { get; set; }
[Parameter]
public string HeaderClass { get; set; }
[Parameter]
public string BodyClass { get; set; }
protected override async Task OnParametersSetAsync()
protected string Category
{
if (string.IsNullOrEmpty(ButtonClass))
get => _category;
private set
{
ButtonClass = "btn-outline-secondary";
if (_category != value)
{
_category = value;
_moduleDefinitions = _allModuleDefinitions.Where(item => item.Categories.Contains(Category)).ToList();
ModuleDefinitionName = "-";
Description = "";
StateHasChanged();
_ = UpdateSettingsAsync();
}
}
}
if (string.IsNullOrEmpty(CardClass))
protected string Pane
{
get => _pane;
private set
{
CardClass = "card border-secondary mb-3";
if (_pane != value)
{
_pane = value;
_ = UpdateSettingsAsync();
}
}
}
if (string.IsNullOrEmpty(HeaderClass))
{
HeaderClass = "card-header";
}
if (string.IsNullOrEmpty(BodyClass))
{
BodyClass = "card-body";
}
protected string Description { get; private set; } = "";
if (UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, PageState.Page.Permissions))
protected string Title { get; private set; } = "";
protected string ContainerType { get; private set; } = "";
protected string Message { get; private set; } = "";
[Parameter]
public string ButtonClass { get; set; } = "btn-outline-secondary";
[Parameter]
public string CardClass { get; set; } = "card border-secondary mb-3";
[Parameter]
public string HeaderClass { get; set; } = "card-header";
[Parameter]
public string BodyClass { get; set; } = "card-body";
protected override async Task OnInitializedAsync()
{
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions))
{
_pages?.Clear();
foreach (Page p in PageState.Pages)
{
if (UserSecurity.IsAuthorized(PageState.User,PermissionNames.View, p.Permissions))
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions))
{
_pages.Add(p);
}
}
await LoadSettingsAsync();
var panes = PageState.Page.Panes;
_pane = panes.Count() == 1 ? panes.SingleOrDefault() : "";
var themes = await ThemeService.GetThemesAsync();
_containers = ThemeService.GetContainerTypes(themes);
_containerType = PageState.Site.DefaultContainerType;
_containers = ThemeService.GetContainerControls(themes, PageState.Page.ThemeType);
ContainerType = PageState.Site.DefaultContainerType;
_allModuleDefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync(PageState.Site.SiteId);
_categories = new List<string>();
foreach (ModuleDefinition moduledefinition in _allModuleDefinitions)
{
if (moduledefinition.Categories != "")
{
foreach (string category in moduledefinition.Categories.Split(','))
{
if (!_categories.Contains(category))
{
_categories.Add(category);
}
}
}
}
_category = "Common";
_moduleDefinitions = _allModuleDefinitions.Where(item => item.Categories.Contains(_category)).ToList();
_moduleDefinitionName = "-";
_description = "";
_moduleDefinitions = _allModuleDefinitions.Where(item => item.Categories.Contains(Category)).ToList();
_categories = _allModuleDefinitions.SelectMany(m => m.Categories.Split(',')).Distinct().ToList();
}
}
private void CategoryChanged(ChangeEventArgs e)
{
_category = (string) e.Value;
_moduleDefinitions = _allModuleDefinitions.Where(item => item.Categories.Contains(_category)).ToList();
_moduleDefinitionName = "-";
_description = "";
StateHasChanged();
Category = (string) e.Value;
}
private void ModuleChanged(ChangeEventArgs e)
{
_moduleDefinitionName = (string)e.Value;
if (_moduleDefinitionName != "-")
ModuleDefinitionName = (string) e.Value;
if (ModuleDefinitionName != "-")
{
var moduleDefinition = _moduleDefinitions.FirstOrDefault(item => item.ModuleDefinitionName == _moduleDefinitionName);
_description = "<br /><div class=\"alert alert-info\" role=\"alert\">" + moduleDefinition.Description + "</div>";
var moduleDefinition = _moduleDefinitions.FirstOrDefault(item => item.ModuleDefinitionName == ModuleDefinitionName);
Description = "<br /><div class=\"alert alert-info\" role=\"alert\">" + moduleDefinition.Description + "</div>";
}
else
{
_description = "";
Description = "";
}
StateHasChanged();
}
private void PageChanged(ChangeEventArgs e)
{
_pageId = (string) e.Value;
_modules?.Clear();
if (_pageId != "-")
PageId = (string) e.Value;
if (PageId != "-")
{
foreach (Module module in PageState.Modules.Where(item => item.PageId == int.Parse(_pageId) && !item.IsDeleted))
{
if (UserSecurity.IsAuthorized(PageState.User,PermissionNames.View, module.Permissions))
{
_modules.Add(module);
}
}
_modules = PageState.Modules
.Where(module => module.PageId == int.Parse(PageId)
&& !module.IsDeleted
&& UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, module.Permissions))
.ToList();
}
_moduleId = "-";
ModuleId = "-";
StateHasChanged();
}
private async Task AddModule()
{
if (UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, PageState.Page.Permissions))
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions))
{
if ((_moduleType == "new" && _moduleDefinitionName != "-") || (_moduleType != "new" && _moduleId != "-"))
if ((ModuleType == "new" && ModuleDefinitionName != "-") || (ModuleType != "new" && ModuleId != "-"))
{
if (_moduleType == "new")
if (ModuleType == "new")
{
Module module = new Module();
module.SiteId = PageState.Site.SiteId;
module.PageId = PageState.Page.PageId;
module.ModuleDefinitionName = _moduleDefinitionName;
module.ModuleDefinitionName = ModuleDefinitionName;
module.AllPages = false;
module.Permissions = PageState.Page.Permissions;
module = await ModuleService.AddModuleAsync(module);
_moduleId = module.ModuleId.ToString();
ModuleId = module.ModuleId.ToString();
}
var pageModule = new PageModule
{
PageId = PageState.Page.PageId,
ModuleId = int.Parse(_moduleId),
Title = _title
ModuleId = int.Parse(ModuleId),
Title = Title
};
if (pageModule.Title == "")
{
if (_moduleType == "new")
if (ModuleType == "new")
{
pageModule.Title = _moduleDefinitions.FirstOrDefault(item => item.ModuleDefinitionName == _moduleDefinitionName)?.Name;
pageModule.Title = _moduleDefinitions.FirstOrDefault(item => item.ModuleDefinitionName == ModuleDefinitionName)?.Name;
}
else
{
pageModule.Title = _modules.FirstOrDefault(item => item.ModuleId == int.Parse(_moduleId))?.Title;
pageModule.Title = _modules.FirstOrDefault(item => item.ModuleId == int.Parse(ModuleId))?.Title;
}
}
pageModule.Pane = _pane;
pageModule.Pane = Pane;
pageModule.Order = int.MaxValue;
pageModule.ContainerType = _containerType;
pageModule.ContainerType = ContainerType;
if (pageModule.ContainerType == PageState.Site.DefaultContainerType)
{
@ -397,32 +395,23 @@
await PageModuleService.AddPageModuleAsync(pageModule);
await PageModuleService.UpdatePageModuleOrderAsync(pageModule.PageId, pageModule.Pane);
_message = "<br /><div class=\"alert alert-success\" role=\"alert\">Module Added To Page</div>";
_moduleDefinitionName = "-";
_description = "";
_pane = "";
_title = "";
_containerType = "";
_pageId = "-";
_moduleId = "-";
Message = "<br /><div class=\"alert alert-success\" role=\"alert\">Module Added To Page</div>";
NavigationManager.NavigateTo(NavigateUrl());
}
else
{
_message = "<br /><div class=\"alert alert-warning\" role=\"alert\">You Must Select A Module</div>";
Message = "<br /><div class=\"alert alert-warning\" role=\"alert\">You Must Select A Module</div>";
}
}
else
{
_message = "<br /><div class=\"alert alert-error\" role=\"alert\">Not Authorized</div>";
Message = "<br /><div class=\"alert alert-error\" role=\"alert\">Not Authorized</div>";
}
}
private async Task ToggleEditMode(bool EditMode)
{
if (UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, PageState.Page.Permissions))
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions))
{
if (EditMode)
{
@ -432,6 +421,7 @@
{
PageState.EditMode = true;
}
NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, "edit=" + ((PageState.EditMode) ? "1" : "0")));
}
else
@ -447,14 +437,14 @@
private void ShowControlPanel()
{
_message = "";
Message = "";
_display = "width: 25%; min-width: 375px;";
StateHasChanged();
}
private void HideControlPanel()
{
_message = "";
Message = "";
_display = "width: 0%;";
StateHasChanged();
}
@ -503,6 +493,61 @@
}
}
private async void Publish(string action)
{
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions))
{
List<PermissionString> permissions;
if (action == "publish")
{
// publish all modules
foreach (var module in PageState.Modules.Where(item => item.PageId == PageState.Page.PageId))
{
permissions = UserSecurity.GetPermissionStrings(module.Permissions);
foreach (var permissionstring in permissions)
{
if (permissionstring.PermissionName == PermissionNames.View)
{
List<string> ids = permissionstring.Permissions.Split(';').ToList();
if (!ids.Contains(Constants.AllUsersRole)) ids.Add(Constants.AllUsersRole);
if (!ids.Contains(Constants.RegisteredRole)) ids.Add(Constants.RegisteredRole);
permissionstring.Permissions = string.Join(";", ids.ToArray());
}
}
module.Permissions = UserSecurity.SetPermissionStrings(permissions);
await ModuleService.UpdateModuleAsync(module);
}
}
// publish page
var page = PageState.Page;
permissions = UserSecurity.GetPermissionStrings(page.Permissions);
foreach (var permissionstring in permissions)
{
if (permissionstring.PermissionName == PermissionNames.View)
{
List<string> ids = permissionstring.Permissions.Split(';').ToList();
switch (action)
{
case "publish":
if (!ids.Contains(Constants.AllUsersRole)) ids.Add(Constants.AllUsersRole);
if (!ids.Contains(Constants.RegisteredRole)) ids.Add(Constants.RegisteredRole);
break;
case "unpublish":
ids.Remove(Constants.AllUsersRole);
ids.Remove(Constants.RegisteredRole);
break;
}
permissionstring.Permissions = string.Join(";", ids.ToArray());
}
}
page.Permissions = UserSecurity.SetPermissionStrings(permissions);
await PageService.UpdatePageAsync(page);
NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, "reload"));
}
}
private void ConfirmDelete()
{
_deleteConfirmation = !_deleteConfirmation;
@ -536,4 +581,24 @@
}
}
private string settingCategory = "CP-category";
private string settingPane = "CP-pane";
private string _pane = "";
private async Task LoadSettingsAsync()
{
Dictionary<string, string> settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId);
_category = SettingService.GetSetting(settings, settingCategory, "Common");
var pane = SettingService.GetSetting(settings, settingPane, "");
_pane = PageState.Page.Panes.Contains(pane) ? pane : PageState.Page.Panes.FirstOrDefault();
}
private async Task UpdateSettingsAsync()
{
Dictionary<string, string> settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId);
SettingService.SetSetting(settings, settingCategory, _category);
SettingService.SetSetting(settings, settingPane, _pane);
await SettingService.UpdateUserSettingsAsync(settings, PageState.User.UserId);
}
}

View File

@ -1,6 +1,5 @@
@namespace Oqtane.Themes.Controls
@inherits LoginBase
@attribute [OqtaneIgnore]
<span class="app-login">
<AuthorizeView>

View File

@ -1,6 +1,5 @@
@namespace Oqtane.Themes.Controls
@inherits ThemeControlBase
@attribute [OqtaneIgnore]
@if (PageState.Site.LogoFileId != null)
{

View File

@ -1,6 +1,6 @@
@namespace Oqtane.Themes.Controls
@inherits MenuBase
@attribute [OqtaneIgnore]
@if (MenuPages.Any())
{
<span class="app-menu-toggler">
@ -16,7 +16,7 @@
if (p.PageId == PageState.Page.PageId)
{
<li class="nav-item active">
<a class="nav-link" href="@NavigateUrl(p.Path)">
<a class="nav-link" href="@GetUrl(p)" target="@GetTarget(p)" >
@if (p.Icon != string.Empty)
{
<span class="oi oi-@p.Icon" aria-hidden="true"></span>
@ -28,7 +28,7 @@
else
{
<li class="nav-item">
<a class="nav-link" href="@NavigateUrl(p.Path)">
<a class="nav-link" href="@GetUrl(p)" target="@GetTarget(p)" >
@if (p.Icon != string.Empty)
{
<span class="oi oi-@p.Icon" aria-hidden="true"></span>

View File

@ -1,6 +1,6 @@
@namespace Oqtane.Themes.Controls
@inherits MenuBase
@attribute [OqtaneIgnore]
@if (MenuPages.Any())
{
<span class="app-menu-toggler">

View File

@ -2,7 +2,7 @@
@inherits ModuleActionsBase
@attribute [OqtaneIgnore]
@if (PageState.EditMode && !PageState.Page.EditMode && UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, ModuleState.Permissions))
@if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, ModuleState.Permissions))
{
<div class="app-moduleactions">
<a class="nav-link dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"></a>
@ -15,7 +15,18 @@
}
else
{
<a class="dropdown-item" @onclick="(async () => await ModuleAction(action))">@action.Name</a>
<a class="dropdown-item" @onclick="(async () => await ModuleAction(action))">
@if (string.IsNullOrEmpty(action.Icon))
{
@((MarkupString) "&nbsp;&nbsp;");
}
else
{
<span class="oi oi-@action.Icon" aria-hidden="true"></span>
}
&nbsp;
@action.Name
</a>
}
}
</div>

View File

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
using Oqtane.Models;
@ -16,8 +17,9 @@ namespace Oqtane.Themes.Controls
{
[Inject] public NavigationManager NavigationManager { get; set; }
[Inject] public IPageModuleService PageModuleService { get; set; }
[Inject] public IModuleService ModuleService { get; set; }
protected List<ActionViewModel> Actions;
public List<ActionViewModel> Actions;
protected override void OnParametersSet()
{
@ -29,42 +31,52 @@ namespace Oqtane.Themes.Controls
var actionList = new List<ActionViewModel>();
if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, ModuleState.Permissions))
{
actionList.Add(new ActionViewModel {Name = "Manage Settings", Action = async (u, m) => await Settings(u, m)});
actionList.Add(new ActionViewModel {Icon = Icons.Cog, Name = "Manage Settings", Action = async (u, m) => await Settings(u, m)});
if (UserSecurity.GetPermissionStrings(ModuleState.Permissions).FirstOrDefault(item => item.PermissionName == PermissionNames.View).Permissions.Split(';').Contains(Constants.AllUsersRole))
{
actionList.Add(new ActionViewModel {Icon=Icons.CircleX, Name = "Unpublish Module", Action = async (s, m) => await Unpublish(s, m) });
}
else
{
actionList.Add(new ActionViewModel {Icon=Icons.CircleCheck, Name = "Publish Module", Action = async (s, m) => await Publish(s, m) });
}
actionList.Add(new ActionViewModel {Icon=Icons.Trash, Name = "Delete Module", Action = async (u, m) => await DeleteModule(u, m) });
if (ModuleState.ModuleDefinition != null && ModuleState.ModuleDefinition.ServerManagerType != "")
{
actionList.Add(new ActionViewModel {Name = "Import Content", Action = async (u, m) => await EditUrlAsync(u, m.ModuleId, "Import")});
actionList.Add(new ActionViewModel {Name = "Export Content", Action = async (u, m) => await EditUrlAsync(u, m.ModuleId, "Export")});
actionList.Add(new ActionViewModel { Name = "" });
actionList.Add(new ActionViewModel {Icon=Icons.CloudUpload, Name = "Import Content", Action = async (u, m) => await EditUrlAsync(u, m.ModuleId, "Import")});
actionList.Add(new ActionViewModel {Icon = Icons.CloudDownload, Name = "Export Content", Action = async (u, m) => await EditUrlAsync(u, m.ModuleId, "Export")});
}
actionList.Add(new ActionViewModel {Name = "Delete Module", Action = async (u, m) => await DeleteModule(u, m)});
actionList.Add(new ActionViewModel {Name = ""});
if (ModuleState.PaneModuleIndex > 0)
{
actionList.Add(new ActionViewModel {Name = "Move To Top", Action = async (s, m) => await MoveTop(s, m)});
actionList.Add(new ActionViewModel {Icon = Icons.DataTransferUpload ,Name = "Move To Top", Action = async (s, m) => await MoveTop(s, m)});
}
if (ModuleState.PaneModuleIndex > 0)
{
actionList.Add(new ActionViewModel {Name = "Move Up", Action = async (s, m) => await MoveUp(s, m)});
actionList.Add(new ActionViewModel {Icon = Icons.ArrowThickTop, Name = "Move Up", Action = async (s, m) => await MoveUp(s, m)});
}
if (ModuleState.PaneModuleIndex < (ModuleState.PaneModuleCount - 1))
{
actionList.Add(new ActionViewModel {Name = "Move Down", Action = async (s, m) => await MoveDown(s, m)});
actionList.Add(new ActionViewModel {Icon = Icons.ArrowThickBottom, Name = "Move Down", Action = async (s, m) => await MoveDown(s, m)});
}
if (ModuleState.PaneModuleIndex < (ModuleState.PaneModuleCount - 1))
{
actionList.Add(new ActionViewModel {Name = "Move To Bottom", Action = async (s, m) => await MoveBottom(s, m)});
actionList.Add(new ActionViewModel {Icon = Icons.DataTransferDownload, Name = "Move To Bottom", Action = async (s, m) => await MoveBottom(s, m)});
}
foreach (string pane in PageState.Page.Panes)
{
if (pane != ModuleState.Pane)
{
actionList.Add(new ActionViewModel {Name = "Move To " + pane + " Pane", Action = async (s, m) => await MoveToPane(s, pane, m)});
actionList.Add(new ActionViewModel {Icon = Icons.AccountLogin, Name = "Move To " + pane + " Pane", Action = async (s, m) => await MoveToPane(s, pane, m)});
}
}
}
@ -121,6 +133,42 @@ namespace Oqtane.Themes.Controls
return url;
}
private async Task<string> Publish(string s, PageModule pagemodule)
{
var permissions = UserSecurity.GetPermissionStrings(pagemodule.Module.Permissions);
foreach (var permissionstring in permissions)
{
if (permissionstring.PermissionName == PermissionNames.View)
{
List<string> ids = permissionstring.Permissions.Split(';').ToList();
if (!ids.Contains(Constants.AllUsersRole)) ids.Add(Constants.AllUsersRole);
if (!ids.Contains(Constants.RegisteredRole)) ids.Add(Constants.RegisteredRole);
permissionstring.Permissions = string.Join(";", ids.ToArray());
}
}
pagemodule.Module.Permissions = UserSecurity.SetPermissionStrings(permissions);
await ModuleService.UpdateModuleAsync(pagemodule.Module);
return NavigateUrl(s, "reload");
}
private async Task<string> Unpublish(string s, PageModule pagemodule)
{
var permissions = UserSecurity.GetPermissionStrings(pagemodule.Module.Permissions);
foreach (var permissionstring in permissions)
{
if (permissionstring.PermissionName == PermissionNames.View)
{
List<string> ids = permissionstring.Permissions.Split(';').ToList();
ids.Remove(Constants.AllUsersRole);
ids.Remove(Constants.RegisteredRole);
permissionstring.Permissions = string.Join(";", ids.ToArray());
}
}
pagemodule.Module.Permissions = UserSecurity.SetPermissionStrings(permissions);
await ModuleService.UpdateModuleAsync(pagemodule.Module);
return NavigateUrl(s, "reload");
}
private async Task<string> MoveTop(string s, PageModule pagemodule)
{
pagemodule.Order = 0;
@ -155,8 +203,8 @@ namespace Oqtane.Themes.Controls
public class ActionViewModel
{
public string Icon { get; set; }
public string Name { set; get; }
public Func<string, PageModule, Task<string>> Action { set; get; }
}
}

View File

@ -1,6 +1,6 @@
@namespace Oqtane.Themes.Controls
@inherits ThemeControlBase
@attribute [OqtaneIgnore]
@inject NavigationManager NavigationManager
<span class="app-profile">

View File

@ -2,5 +2,6 @@
{
public interface IContainerControl
{
}
}

View File

@ -2,7 +2,6 @@
{
public interface ILayoutControl
{
string Panes { get; } // identifies all panes in a theme ( delimited by ";" )
}
}

View File

@ -1,19 +1,7 @@
using Microsoft.AspNetCore.Components;
using Oqtane.Shared;
using Oqtane.UI;
namespace Oqtane.Themes
namespace Oqtane.Themes
{
public class LayoutBase : ComponentBase, ILayoutControl
public abstract class LayoutBase : ThemeBase, ILayoutControl
{
[CascadingParameter]
protected PageState PageState { get; set; }
public virtual string Panes { get; set; }
public string LayoutPath()
{
return "Themes/" + GetType().Namespace + "/";
}
}
}

View File

@ -13,3 +13,7 @@
</div>
</div>
</div>
@code {
public override string Name => "Standard Header";
}

View File

@ -17,14 +17,16 @@
</main>
@code {
public override string Name => "Default";
public override string Panes => string.Empty;
public override List<Resource> Resources => new List<Resource>()
{
new Resource { ResourceType = ResourceType.Stylesheet, Url = ThemePath() + "BootswatchCyborg.css" },
// remote stylesheets can be linked using the format below, however we want the default theme to display properly in local development scenarios where an Internet connection is not available
//new Resource { ResourceType = ResourceType.Stylesheet, Url = "https://stackpath.bootstrapcdn.com/bootswatch/4.4.1/cyborg/bootstrap.min.css", Integrity = "sha384-l7xaoY0cJM4h9xh1RfazbgJVUZvdtyLWPueWNtLAphf/UbBgOVzqbOTogxPwYLHM", CrossOrigin = "anonymous" },
new Resource { ResourceType = ResourceType.Stylesheet, Url = ThemePath() + "Theme.css" }
new Resource { ResourceType = ResourceType.Stylesheet, Url = "https://stackpath.bootstrapcdn.com/bootswatch/4.5.0/cyborg/bootstrap.min.css", Integrity = "sha384-GKugkVcT8wqoh3M8z1lqHbU+g6j498/ZT/zuXbepz7Dc09/otQZxTimkEMTkRWHP", CrossOrigin = "anonymous" },
new Resource { ResourceType = ResourceType.Stylesheet, Url = ThemePath() + "Theme.css" },
new Resource { ResourceType = ResourceType.Script, Bundle = "Bootstrap", Url = "https://code.jquery.com/jquery-3.5.1.slim.min.js", Integrity = "sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj", CrossOrigin = "anonymous" },
new Resource { ResourceType = ResourceType.Script, Bundle = "Bootstrap", Url = "https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js", Integrity = "sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo", CrossOrigin = "anonymous" },
new Resource { ResourceType = ResourceType.Script, Bundle = "Bootstrap", Url = "https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js", Integrity = "sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI", CrossOrigin = "anonymous" }
};
}

View File

@ -14,5 +14,7 @@
</div>
@code {
public override string Panes => "Top;Left;Content;Right;Bottom";
public override string Name => "Multiple Panes";
public override string Panes => "Top,Left,Content,Right,Bottom";
}

View File

@ -7,3 +7,7 @@
}
<ModuleInstance />
</div>
@code {
public override string Name => "No Header";
}

View File

@ -6,5 +6,7 @@
</div>
@code {
public override string Name => "Single Pane";
public override string Panes => "Content";
}

View File

@ -4,11 +4,12 @@ using Oqtane.Models;
using Oqtane.Shared;
using Oqtane.UI;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Oqtane.Themes
{
public class ThemeBase : ComponentBase, IThemeControl
public abstract class ThemeBase : ComponentBase, IThemeControl
{
[Inject]
protected IJSRuntime JSRuntime { get; set; }
@ -17,9 +18,30 @@ namespace Oqtane.Themes
[CascadingParameter]
protected PageState PageState { get; set; }
public virtual string Name { get; set; }
public virtual string Thumbnail { get; set; }
public virtual string Panes { get; set; }
public virtual List<Resource> Resources { get; set; }
// base lifecycle method for handling JSInterop script registration
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
if (Resources != null && Resources.Exists(item => item.ResourceType == ResourceType.Script))
{
var scripts = new List<object>();
foreach (Resource resource in Resources.Where(item => item.ResourceType == ResourceType.Script))
{
scripts.Add(new { href = resource.Url, bundle = resource.Bundle ?? "", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "" });
}
var interop = new Interop(JSRuntime);
await interop.IncludeScripts(scripts.ToArray());
}
}
}
// path method
public string ThemePath()
@ -58,5 +80,10 @@ namespace Oqtane.Themes
{
return Utilities.EditUrl(PageState.Alias.Path, path, moduleid, action, parameters);
}
public string ContentUrl(int fileid)
{
return Utilities.ContentUrl(PageState.Alias, fileid);
}
}
}

View File

@ -1,47 +1,10 @@
using Microsoft.AspNetCore.Components;
using Oqtane.Shared;
using Oqtane.UI;
using Oqtane.Shared;
namespace Oqtane.Themes
{
public class ThemeControlBase : ComponentBase
[OqtaneIgnore]
public abstract class ThemeControlBase : ThemeBase
{
[CascadingParameter]
protected PageState PageState { get; set; }
public string NavigateUrl()
{
return NavigateUrl(PageState.Page.Path);
}
public string NavigateUrl(string path)
{
return NavigateUrl(path, "");
}
public string NavigateUrl(string path, string parameters)
{
return Utilities.NavigateUrl(PageState.Alias.Path, path, parameters);
}
public string EditUrl(int moduleid, string action)
{
return EditUrl(moduleid, action, "");
}
public string EditUrl(int moduleid, string action, string parameters)
{
return EditUrl(PageState.Page.Path, moduleid, action, parameters);
}
public string EditUrl(string path, int moduleid, string action, string parameters)
{
return Utilities.EditUrl(PageState.Alias.Path, path, moduleid, action, parameters);
}
public string ContentUrl(int fileid)
{
return Utilities.ContentUrl(PageState.Alias, fileid);
}
}
}

View File

@ -19,7 +19,7 @@
{
_moduleState = Module; // passed in from Pane component
string container = _moduleState.ContainerType;
if (PageState.ModuleId != -1 && PageState.Action != "" && _moduleState.UseAdminContainer)
if (PageState.ModuleId != -1 && _moduleState.UseAdminContainer)
{
container = Constants.DefaultAdminContainer;
}
@ -27,18 +27,13 @@
DynamicComponent = builder =>
{
Type containerType = Type.GetType(container);
if (containerType != null)
if (containerType == null)
{
builder.OpenComponent(0, containerType);
builder.CloseComponent();
}
else
{
// container does not exist with type specified
builder.OpenComponent(0, Type.GetType(Constants.ModuleMessageComponent));
builder.AddAttribute(1, "Message", "Error Loading Module Container " + container);
builder.CloseComponent();
// fallback
containerType = Type.GetType(Constants.DefaultContainer);
}
builder.OpenComponent(0, containerType);
builder.CloseComponent();
};
}
}

View File

@ -3,6 +3,7 @@
@inject IInstallationService InstallationService
@inject ISiteService SiteService
@inject IUserService UserService
@inject IJSRuntime JSRuntime
<div class="container">
<div class="row">
@ -138,6 +139,15 @@
private string _integratedSecurityDisplay = "display: none;";
private string _loadingDisplay = "display: none;";
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
var interop = new Interop(JSRuntime);
await interop.IncludeLink("app-stylesheet", "stylesheet", "https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css", "text/css", "sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T", "anonymous", "");
}
}
private void SetIntegratedSecurity(ChangeEventArgs e)
{
_integratedSecurityDisplay = Convert.ToBoolean((string)e.Value)

View File

@ -1,6 +1,5 @@
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using System;
using Microsoft.JSInterop;
using Oqtane.Models;
using System.Threading.Tasks;
namespace Oqtane.UI
@ -18,9 +17,9 @@ namespace Oqtane.UI
{
try
{
_jsRuntime.InvokeAsync<string>(
"interop.setCookie",
name, value, days);
_jsRuntime.InvokeVoidAsync(
"Oqtane.Interop.setCookie",
name, value, days);
return Task.CompletedTask;
}
catch
@ -34,7 +33,7 @@ namespace Oqtane.UI
try
{
return _jsRuntime.InvokeAsync<string>(
"interop.getCookie",
"Oqtane.Interop.getCookie",
name);
}
catch
@ -47,8 +46,8 @@ namespace Oqtane.UI
{
try
{
_jsRuntime.InvokeAsync<string>(
"interop.updateTitle",
_jsRuntime.InvokeVoidAsync(
"Oqtane.Interop.updateTitle",
title);
return Task.CompletedTask;
}
@ -58,13 +57,13 @@ namespace Oqtane.UI
}
}
public Task IncludeMeta(string id, string attribute, string name, string content)
public Task IncludeMeta(string id, string attribute, string name, string content, string key)
{
try
{
_jsRuntime.InvokeAsync<string>(
"interop.includeMeta",
id, attribute, name, content);
_jsRuntime.InvokeVoidAsync(
"Oqtane.Interop.includeMeta",
id, attribute, name, content, key);
return Task.CompletedTask;
}
catch
@ -73,13 +72,13 @@ namespace Oqtane.UI
}
}
public Task IncludeLink(string id, string rel, string url, string type, string integrity, string crossorigin)
public Task IncludeLink(string id, string rel, string href, string type, string integrity, string crossorigin, string key)
{
try
{
_jsRuntime.InvokeAsync<string>(
"interop.includeLink",
id, rel, url, type, integrity, crossorigin);
_jsRuntime.InvokeVoidAsync(
"Oqtane.Interop.includeLink",
id, rel, href, type, integrity, crossorigin, key);
return Task.CompletedTask;
}
catch
@ -88,13 +87,13 @@ namespace Oqtane.UI
}
}
public Task IncludeScript(string id, string src, string content, string location, string integrity, string crossorigin)
public Task IncludeLinks(object[] links)
{
try
{
_jsRuntime.InvokeAsync<string>(
"interop.includeScript",
id, src, content, location, integrity, crossorigin);
_jsRuntime.InvokeVoidAsync(
"Oqtane.Interop.includeLinks",
(object) links);
return Task.CompletedTask;
}
catch
@ -103,13 +102,13 @@ namespace Oqtane.UI
}
}
public Task IncludeCSS(string id, string url)
public Task IncludeScript(string id, string src, string integrity, string crossorigin, string content, string location, string key)
{
try
{
_jsRuntime.InvokeAsync<string>(
"interop.includeLink",
id, "stylesheet", url, "text/css");
_jsRuntime.InvokeVoidAsync(
"Oqtane.Interop.includeScript",
id, src, integrity, crossorigin, content, location, key);
return Task.CompletedTask;
}
catch
@ -118,12 +117,26 @@ namespace Oqtane.UI
}
}
public async Task IncludeScripts(object[] scripts)
{
try
{
await _jsRuntime.InvokeVoidAsync(
"Oqtane.Interop.includeScripts",
(object)scripts);
}
catch
{
// ignore exception
}
}
public Task RemoveElementsById(string prefix, string first, string last)
{
try
{
_jsRuntime.InvokeAsync<string>(
"interop.removeElementsById",
_jsRuntime.InvokeVoidAsync(
"Oqtane.Interop.removeElementsById",
prefix, first, last);
return Task.CompletedTask;
}
@ -133,13 +146,12 @@ namespace Oqtane.UI
}
}
public ValueTask<string> GetElementByName(string name)
{
try
{
return _jsRuntime.InvokeAsync<string>(
"interop.getElementByName",
"Oqtane.Interop.getElementByName",
name);
}
catch
@ -152,9 +164,9 @@ namespace Oqtane.UI
{
try
{
_jsRuntime.InvokeAsync<string>(
"interop.submitForm",
path, fields);
_jsRuntime.InvokeVoidAsync(
"Oqtane.Interop.submitForm",
path, fields);
return Task.CompletedTask;
}
catch
@ -168,7 +180,7 @@ namespace Oqtane.UI
try
{
return _jsRuntime.InvokeAsync<string[]>(
"interop.getFiles",
"Oqtane.Interop.getFiles",
id);
}
catch
@ -181,9 +193,9 @@ namespace Oqtane.UI
{
try
{
_jsRuntime.InvokeAsync<string>(
"interop.uploadFiles",
posturl, folder, id);
_jsRuntime.InvokeVoidAsync(
"Oqtane.Interop.uploadFiles",
posturl, folder, id);
return Task.CompletedTask;
}
catch
@ -191,5 +203,36 @@ namespace Oqtane.UI
return Task.CompletedTask;
}
}
public Task RefreshBrowser(bool force, int wait)
{
try
{
_jsRuntime.InvokeVoidAsync(
"Oqtane.Interop.refreshBrowser",
force, wait);
return Task.CompletedTask;
}
catch
{
return Task.CompletedTask;
}
}
public Task RedirectBrowser(string url, int wait)
{
try
{
_jsRuntime.InvokeVoidAsync(
"Oqtane.Interop.redirectBrowser",
url, wait);
return Task.CompletedTask;
}
catch
{
return Task.CompletedTask;
}
}
}
}

View File

@ -14,6 +14,7 @@ namespace Oqtane.UI
public List<Module> Modules { get; set; }
public Uri Uri { get; set; }
public Dictionary<string, string> QueryString { get; set; }
public string UrlParameters { get; set; }
public int ModuleId { get; set; }
public string Action { get; set; }
public bool EditMode { get; set; }

View File

@ -1,4 +1,5 @@
@namespace Oqtane.UI
@using Microsoft.AspNetCore.Components.Rendering
@namespace Oqtane.UI
@inject IUserService UserService
@inject IModuleService ModuleService
@inject IModuleDefinitionService ModuleDefinitionService
@ -25,7 +26,7 @@
protected override void OnParametersSet()
{
if (PageState.EditMode && !PageState.Page.EditMode && UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, PageState.Page.Permissions) && Name != Constants.AdminPane)
if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, PageState.Page.Permissions) && Name != Constants.AdminPane)
{
_paneadminborder = "app-pane-admin-border";
_panetitle = "<div class=\"app-pane-admin-title\">" + Name + " Pane</div>";
@ -38,12 +39,12 @@
DynamicComponent = builder =>
{
if (PageState.ModuleId != -1 && PageState.Action != "")
if (PageState.ModuleId != -1 && PageState.Action != Constants.DefaultAction)
{
if (Name.ToLower() == Constants.AdminPane.ToLower())
{
Module module = PageState.Modules.FirstOrDefault(item => item.ModuleId == PageState.ModuleId);
if (module != null)
if (module != null && !module.IsDeleted)
{
var typename = module.ModuleType;
// check for core module actions component
@ -88,10 +89,7 @@
{
module.Title = module.ControlTitle;
}
builder.OpenComponent(0, Type.GetType(Constants.ContainerComponent));
builder.AddAttribute(1, "Module", module);
builder.CloseComponent();
CreateComponent(builder, module);
}
}
else
@ -106,14 +104,12 @@
if (PageState.ModuleId != -1)
{
Module module = PageState.Modules.FirstOrDefault(item => item.ModuleId == PageState.ModuleId);
if (module != null && module.Pane.ToLower() == Name.ToLower())
if (module != null && module.Pane.ToLower() == Name.ToLower() && !module.IsDeleted)
{
// check if user is authorized to view module
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, module.Permissions))
{
builder.OpenComponent(0, Type.GetType(Constants.ContainerComponent));
builder.AddAttribute(1, "Module", module);
builder.CloseComponent();
CreateComponent(builder, module);
}
}
}
@ -124,14 +120,19 @@
// check if user is authorized to view module
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, module.Permissions))
{
builder.OpenComponent(0, Type.GetType(Constants.ContainerComponent));
builder.AddAttribute(1, "Module", module);
builder.SetKey(module.PageModuleId);
builder.CloseComponent();
CreateComponent(builder, module);
}
}
}
}
};
}
private void CreateComponent(RenderTreeBuilder builder, Module module)
{
builder.OpenComponent(0, Type.GetType(Constants.ContainerComponent));
builder.AddAttribute(1, "Module", module);
builder.SetKey(module.PageModuleId);
builder.CloseComponent();
}
}

View File

@ -13,15 +13,13 @@
DynamicComponent = builder =>
{
var layoutType = Type.GetType(PageState.Page.LayoutType);
if (layoutType != null)
if (layoutType == null)
{
builder.OpenComponent(0, layoutType);
builder.CloseComponent();
}
else
{
// layout does not exist with type specified
// fallback
layoutType = Type.GetType(Constants.DefaultLayout);
}
builder.OpenComponent(0, layoutType);
builder.CloseComponent();
};
}
}

View File

@ -76,7 +76,8 @@
User user = null;
List<Module> modules;
var moduleid = -1;
var action = "";
var action = string.Empty;
var urlparameters = string.Empty;
var editmode = false;
var reload = Reload.None;
var lastsyncdate = DateTime.UtcNow;
@ -90,7 +91,7 @@
// parse querystring
var querystring = ParseQueryString(uri.Query);
// the reload parameter is used during user login/logout
// the reload parameter is used to reload the PageState
if (querystring.ContainsKey("reload"))
{
reload = Reload.Site;
@ -173,27 +174,66 @@
if (alias.Path != "")
{
path = path.Replace(alias.Path + "/", "");
path = path.Substring(alias.Path.Length + 1);
}
// extract admin route elements from path
var segments = path.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
int result;
// check if path has moduleid and action specification ie. pagename/moduleid/action/
if (segments.Length >= 2 && int.TryParse(segments[segments.Length - 2], out result))
int modIdPos = 0;
int actionPos = 0;
int urlParametersPos = 0;
for (int i = 0; i < segments.Length; i++)
{
action = segments[segments.Length - 1];
moduleid = result;
path = path.Replace(moduleid.ToString() + "/" + action + "/", "");
}
else
{
// check if path has moduleid specification ie. pagename/moduleid/
if (segments.Length >= 1 && int.TryParse(segments[segments.Length - 1], out result))
if (segments[i] == Constants.UrlParametersDelimiter)
{
moduleid = result;
path = path.Replace(moduleid.ToString() + "/", "");
urlParametersPos = i + 1;
}
if (i >= urlParametersPos && urlParametersPos != 0)
{
urlparameters += "/" + segments[i];
}
if (segments[i] == Constants.ModuleDelimiter)
{
modIdPos = i + 1;
actionPos = modIdPos + 1;
if (actionPos > segments.Length - 1)
{
action = Constants.DefaultAction;
}
else
{
action = segments[actionPos];
}
}
}
// check if path has moduleid and action specification ie. pagename/moduleid/action/
if (modIdPos > 0)
{
int.TryParse(segments[modIdPos], out result);
moduleid = result;
if (actionPos > segments.Length - 1)
{
path = path.Replace(segments[modIdPos - 1] + "/" + segments[modIdPos] + "/", "");
}
else
{
path = path.Replace(segments[modIdPos - 1] + "/" + segments[modIdPos] + "/" + segments[actionPos] + "/", "");
}
}
if (urlParametersPos > 0)
{
path = path.Replace(segments[urlParametersPos - 1] + urlparameters + "/", "");
}
// remove trailing slash so it can be used as a key for Pages
@ -220,17 +260,14 @@
{
page = pages.Where(item => item.Path == path).FirstOrDefault();
reload = Reload.Page;
if (page != null)
{
editmode = page.EditMode;
}
editmode = false;
}
if (page != null)
{
if (PageState == null)
{
editmode = page.EditMode;
editmode = false;
}
// check if user is authorized to view page
@ -263,6 +300,7 @@
Modules = modules,
Uri = new Uri(_absoluteUri, UriKind.Absolute),
QueryString = querystring,
UrlParameters = urlparameters,
ModuleId = moduleid,
Action = action,
EditMode = editmode,
@ -277,7 +315,6 @@
{
if (user == null)
{
await LogService.Log(null, null, null, GetType().AssemblyQualifiedName, Utilities.GetTypeNameLastSegment(GetType().AssemblyQualifiedName, 1), LogFunction.Security, LogLevel.Error, null, "Page Does Not Exist Or User Is Not Authorized To View Page {Path}", path);
// redirect to login page
NavigationManager.NavigateTo(Utilities.NavigateUrl(alias.Path, "login", "returnurl=" + path));
}
@ -361,20 +398,33 @@
string panes = "";
Type themetype = Type.GetType(page.ThemeType);
var themeobject = Activator.CreateInstance(themetype) as IThemeControl;
if (themeobject != null)
if (themetype == null)
{
panes = themeobject.Panes;
page.Resources = ManagePageResources(page.Resources, themeobject.Resources);
// fallback
page.ThemeType = Constants.DefaultTheme;
page.LayoutType = Constants.DefaultLayout;
themetype = Type.GetType(Constants.DefaultTheme);
}
if (themetype != null)
{
var themeobject = Activator.CreateInstance(themetype) as IThemeControl;
if (themeobject != null)
{
panes = themeobject.Panes;
page.Resources = ManagePageResources(page.Resources, themeobject.Resources);
}
}
if (!string.IsNullOrEmpty(page.LayoutType))
{
Type layouttype = Type.GetType(page.LayoutType);
var layoutobject = Activator.CreateInstance(layouttype) as ILayoutControl;
if (layoutobject != null)
if (layouttype != null)
{
panes = layoutobject.Panes;
var layoutobject = Activator.CreateInstance(layouttype) as IThemeControl;
if (layoutobject != null)
{
panes = layoutobject.Panes;
}
}
}
@ -396,7 +446,7 @@
if (module.PageId == page.PageId || module.ModuleId == moduleid)
{
var typename = string.Empty;
if (module.ModuleDefinition != null)
if (module.ModuleDefinition != null && (module.ModuleDefinition.Runtimes == "" || module.ModuleDefinition.Runtimes.Contains(GetRuntime().ToString())))
{
typename = module.ModuleDefinition.ControlTypeTemplate;
}

View File

@ -13,6 +13,13 @@
{
var interop = new Interop(JsRuntime);
// handle page redirection
if (!string.IsNullOrEmpty(PageState.Page.Url))
{
NavigationManager.NavigateTo(PageState.Page.Url);
return;
}
// set page title
if (!string.IsNullOrEmpty(PageState.Page.Title))
{
@ -23,31 +30,20 @@
await interop.UpdateTitle(PageState.Site.Name + " - " + PageState.Page.Name);
}
// update page resources
int stylesheet = 0;
int script = 0;
foreach (Resource resource in PageState.Page.Resources)
// manage stylesheets for this page
string batch = DateTime.Now.ToString("yyyyMMddHHmmssfff");
var links = new List<object>();
foreach (Resource resource in PageState.Page.Resources.Where(item => item.ResourceType == ResourceType.Stylesheet))
{
switch (resource.ResourceType)
{
case ResourceType.Stylesheet:
stylesheet += 1;
await interop.IncludeLink("app-stylesheet" + stylesheet.ToString("00"), "stylesheet", resource.Url, "text/css", resource.Integrity ?? "", resource.CrossOrigin ?? "");
break;
case ResourceType.Script:
script += 1;
await interop.IncludeScript("app-script" + script.ToString("00"), resource.Url, "", "body", resource.Integrity ?? "", resource.CrossOrigin ?? "");
break;
}
links.Add(new { id = "app-stylesheet-" + batch + "-" + (links.Count + 1).ToString("00"), rel = "stylesheet", href = resource.Url, type = "text/css", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", key = "" });
}
// remove any page resources references which are no longer required for this page
await interop.RemoveElementsById("app-stylesheet", "app-stylesheet" + (stylesheet + 1).ToString("00"), "");
await interop.RemoveElementsById("app-script", "app-script" + (script + 1).ToString("00"), "");
await interop.IncludeLinks(links.ToArray());
await interop.RemoveElementsById("app-stylesheet", "", "app-stylesheet-" + batch + "-00");
// add favicon
if (PageState.Site.FaviconFileId != null)
{
await interop.IncludeLink("fav-icon", "shortcut icon", Utilities.ContentUrl(PageState.Alias, PageState.Site.FaviconFileId.Value), "image/x-icon", "", "");
await interop.IncludeLink("app-favicon", "shortcut icon", Utilities.ContentUrl(PageState.Alias, PageState.Site.FaviconFileId.Value), "image/x-icon", "", "", "id");
}
// add PWA support
if (PageState.Site.PwaIsEnabled)
@ -58,18 +54,8 @@
DynamicComponent = builder =>
{
var themeType = Type.GetType(PageState.Page.ThemeType);
if (themeType != null)
{
builder.OpenComponent(0, themeType);
builder.CloseComponent();
}
else
{
// theme does not exist with type specified
builder.OpenComponent(0, Type.GetType(Constants.ModuleMessageComponent));
builder.AddAttribute(1, "Message", "Error Loading Page Theme " + PageState.Page.ThemeType);
builder.CloseComponent();
}
builder.OpenComponent(0, themeType);
builder.CloseComponent();
};
}
@ -97,10 +83,10 @@
"const serialized = JSON.stringify(manifest); " +
"const blob = new Blob([serialized], {type: 'application/javascript'}); " +
"const url = URL.createObjectURL(blob); " +
"document.getElementById('pwa-manifest').setAttribute('href', url); " +
"document.getElementById('app-manifest').setAttribute('href', url); " +
"} " +
", 1000);";
await interop.IncludeScript("pwa-manifestscript", "", manifest, "body", "", "");
await interop.IncludeScript("app-pwa", "", "", "", manifest, "body", "id");
// service worker must be in root of site
string serviceworker = "if ('serviceWorker' in navigator) { " +
@ -110,6 +96,6 @@
"console.log('ServiceWorker Registration Failed ', err); " +
"}); " +
"}";
await interop.IncludeScript("pwa-serviceworker", "", serviceworker, "body", "", "");
await interop.IncludeScript("app-serviceworker", "", "", "", serviceworker, "body", "id");
}
}

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>Oqtane.Client</id>
<version>1.0.3</version>
<authors>Shaun Walker</authors>
<owners>.NET Foundation</owners>
<title>Oqtane Framework</title>
<description>A modular application framework for Blazor</description>
<copyright>.NET Foundation</copyright>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<license type="expression">MIT</license>
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
<iconUrl>https://www.oqtane.org/Portals/0/icon.jpg</iconUrl>
<tags>oqtane</tags>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v1.0.3</releaseNotes>
<summary>A modular application framework for Blazor</summary>
</metadata>
<files>
<file src="..\Oqtane.Client\bin\Release\netstandard2.1\Oqtane.Client.dll" target="lib\netstandard2.1" />
<file src="..\Oqtane.Client\bin\Release\netstandard2.1\Oqtane.Client.pdb" target="lib\netstandard2.1" />
</files>
</package>

View File

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>Oqtane.Framework</id>
<version>1.0.0</version>
<version>1.0.3</version>
<authors>Shaun Walker</authors>
<owners>.NET Foundation</owners>
<title>Oqtane Framework</title>
@ -13,13 +13,11 @@
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
<iconUrl>https://www.oqtane.org/Portals/0/icon.jpg</iconUrl>
<tags>oqtane framework</tags>
<releaseNotes>Initial Release</releaseNotes>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v1.0.3</releaseNotes>
<summary>A modular application framework for Blazor</summary>
</metadata>
<files>
<file src="..\Oqtane.Client\bin\Release\netstandard2.1\Oqtane.Client.dll" target="lib" />
<file src="..\Oqtane.Server\bin\Release\netcoreapp3.1\Oqtane.Server.dll" target="lib" />
<file src="..\Oqtane.Shared\bin\Release\netstandard2.1\Oqtane.Shared.dll" target="lib" />
<file src="..\Oqtane.Server\bin\Release\netcoreapp3.1\Oqtane.Upgrade.dll" target="lib" />
<file src="..\Oqtane.Server\bin\Release\netcoreapp3.1\publish\*.*" target="lib\netcoreapp3.1" />
<file src="..\Oqtane.Server\bin\Release\netcoreapp3.1\publish\wwwroot\**\*.*" target="wwwroot" />
</files>
</package>

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>Oqtane.Server</id>
<version>1.0.3</version>
<authors>Shaun Walker</authors>
<owners>.NET Foundation</owners>
<title>Oqtane Framework</title>
<description>A modular application framework for Blazor</description>
<copyright>.NET Foundation</copyright>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<license type="expression">MIT</license>
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
<iconUrl>https://www.oqtane.org/Portals/0/icon.jpg</iconUrl>
<tags>oqtane</tags>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v1.0.3</releaseNotes>
<summary>A modular application framework for Blazor</summary>
</metadata>
<files>
<file src="..\Oqtane.Server\bin\Release\netcoreapp3.1\Oqtane.Server.dll" target="lib\netcoreapp3.1" />
<file src="..\Oqtane.Server\bin\Release\netcoreapp3.1\Oqtane.Server.pdb" target="lib\netcoreapp3.1" />
</files>
</package>

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>Oqtane.Shared</id>
<version>1.0.3</version>
<authors>Shaun Walker</authors>
<owners>.NET Foundation</owners>
<title>Oqtane Framework</title>
<description>A modular application framework for Blazor</description>
<copyright>.NET Foundation</copyright>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<license type="expression">MIT</license>
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
<iconUrl>https://www.oqtane.org/Portals/0/icon.jpg</iconUrl>
<tags>oqtane</tags>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v1.0.3</releaseNotes>
<summary>A modular application framework for Blazor</summary>
</metadata>
<files>
<file src="..\Oqtane.Shared\bin\Release\netstandard2.1\Oqtane.Shared.dll" target="lib\netstandard2.1" />
<file src="..\Oqtane.Shared\bin\Release\netstandard2.1\Oqtane.Shared.pdb" target="lib\netstandard2.1" />
</files>
</package>

View File

@ -0,0 +1 @@
Compress-Archive -Path "..\Oqtane.Server\bin\Release\netcoreapp3.1\publish\*" -DestinationPath "..\Oqtane.Server\bin\Release\Oqtane.Framework.1.0.3.Install.zip" -Force

Binary file not shown.

View File

@ -1,3 +0,0 @@
DEL "*.nupkg"
nuget.exe pack Oqtane.Framework.nuspec

View File

@ -0,0 +1,18 @@
del "*.nupkg"
dotnet clean -c Release ..\Oqtane.sln
dotnet build -c Release ..\Oqtane.sln
nuget.exe pack Oqtane.Client.nuspec
nuget.exe pack Oqtane.Server.nuspec
nuget.exe pack Oqtane.Shared.nuspec
del /F/Q/S "..\Oqtane.Server\bin\Release\netcoreapp3.1\publish" > NUL
rmdir /Q/S "..\Oqtane.Server\bin\Release\netcoreapp3.1\publish"
dotnet publish ..\Oqtane.Server\Oqtane.Server.csproj /p:Configuration=Release
del "..\Oqtane.Server\bin\Release\netcoreapp3.1\publish\appsettings.json"
ren "..\Oqtane.Server\bin\Release\netcoreapp3.1\publish\appsettings.release.json" "appsettings.json"
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe ".\install.ps1"
del "..\Oqtane.Server\bin\Release\netcoreapp3.1\publish\appsettings.json"
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe ".\upgrade.ps1"
del "..\Oqtane.Server\bin\Release\netcoreapp3.1\publish\Oqtane.Upgrade.*"
nuget.exe pack Oqtane.Framework.nuspec

View File

@ -0,0 +1 @@
Compress-Archive -Path "..\Oqtane.Server\bin\Release\netcoreapp3.1\publish\*" -DestinationPath "..\Oqtane.Server\bin\Release\Oqtane.Framework.1.0.3.Upgrade.zip" -Force

View File

@ -47,7 +47,7 @@ namespace Oqtane.Controllers
}
// GET api/<controller>/name/xxx?sync=yyyyMMddHHmmssfff
[HttpGet("name/{name}")]
[HttpGet("name/{**name}")]
public Alias Get(string name, string sync)
{
List<Alias> aliases = _aliases.GetAliases().ToList(); // cached

View File

@ -1,4 +1,4 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
@ -483,7 +483,7 @@ namespace Oqtane.Controllers
string[] folders = folderpath.Split(separators, StringSplitOptions.RemoveEmptyEntries);
foreach (string folder in folders)
{
path = Utilities.PathCombine(path, folder, "\\");
path = Utilities.PathCombine(path, folder, Path.DirectorySeparatorChar.ToString());
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);

View File

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.IO;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using Oqtane.Models;
@ -112,7 +113,7 @@ namespace Oqtane.Controllers
Folder parent = _folders.GetFolder(folder.ParentId.Value);
folder.Path = Utilities.PathCombine(parent.Path, folder.Name);
}
folder.Path = Utilities.PathCombine(folder.Path, "\\");
folder.Path = Utilities.PathCombine(folder.Path, Path.DirectorySeparatorChar.ToString());
folder = _folders.AddFolder(folder);
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Folder Added {Folder}", folder);
}
@ -147,7 +148,7 @@ namespace Oqtane.Controllers
Folder parent = _folders.GetFolder(folder.ParentId.Value);
folder.Path = Utilities.PathCombine(parent.Path, folder.Name);
}
folder.Path = Utilities.PathCombine(folder.Path, "\\");
folder.Path = Utilities.PathCombine(folder.Path, Path.DirectorySeparatorChar.ToString());
folder = _folders.UpdateFolder(folder);
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Folder Updated {Folder}", folder);
}

View File

@ -9,6 +9,9 @@ using System.IO;
using System.Reflection;
using System.Linq;
using System.IO.Compression;
using Oqtane.Modules;
using Oqtane.Themes;
using System.Diagnostics;
namespace Oqtane.Controllers
{
@ -70,8 +73,27 @@ namespace Oqtane.Controllers
// get list of assemblies which should be downloaded to browser
var assemblies = AppDomain.CurrentDomain.GetOqtaneClientAssemblies();
var list = assemblies.Select(a => a.GetName().Name).ToList();
var deps = assemblies.SelectMany(a => a.GetReferencedAssemblies()).Distinct();
list.AddRange(deps.Where(a => a.Name.EndsWith(".oqtane", StringComparison.OrdinalIgnoreCase)).Select(a => a.Name));
// get module and theme dependencies
foreach (var assembly in assemblies)
{
foreach (var type in assembly.GetTypes().Where(item => item.GetInterfaces().Contains(typeof(IModule))))
{
var instance = Activator.CreateInstance(type) as IModule;
foreach (string name in instance.ModuleDefinition.Dependencies.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
if (!list.Contains(name)) list.Add(name);
}
}
foreach (var type in assembly.GetTypes().Where(item => item.GetInterfaces().Contains(typeof(ITheme))))
{
var instance = Activator.CreateInstance(type) as ITheme;
foreach (string name in instance.Theme.Dependencies.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
if (!list.Contains(name)) list.Add(name);
}
}
}
// create zip file containing assemblies and debug symbols
string binfolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
@ -90,6 +112,7 @@ namespace Oqtane.Controllers
filestream.CopyTo(entrystream);
}
// include debug symbols ( we may want to consider restricting this to only host users or when running in debug mode for performance )
if (System.IO.File.Exists(Path.Combine(binfolder, file + ".pdb")))
{
entry = archive.CreateEntry(file + ".pdb");

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