Compare commits

..

281 Commits

Author SHA1 Message Date
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
19fb3120d3 Update README.md 2020-05-19 14:18:43 -04:00
35b26c7525 update to version 1.0.0 2020-05-19 14:08:03 -04:00
4154bd0988 Merge pull request #509 from sbwalker/master
update to version 1.0.0
2020-05-19 14:07:10 -04:00
52b2c876a4 performance optimizations to avoid use of reflection ( thanks to @chlupac for this suggestion ) 2020-05-19 13:39:39 -04:00
b7eeda7562 Merge pull request #508 from sbwalker/master
performance optimizations to avoid use of reflection ( thanks to @chlupac for this suggestion )
2020-05-19 13:38:30 -04:00
b59e2533ea update to .NET Core 3.2 2020-05-19 12:48:30 -04:00
a8ec5e1265 Merge pull request #507 from sbwalker/master
update to .NET Core 3.2
2020-05-19 12:47:36 -04:00
61ff097f51 default theme needs to work in local development scenarios 2020-05-19 09:17:38 -04:00
b82f69b8b0 Merge pull request #506 from sbwalker/master
default theme needs to work in local development scenarios
2020-05-19 09:16:37 -04:00
263b045c75 module creator template updates 2020-05-19 09:08:29 -04:00
a160597f6f Merge pull request #505 from sbwalker/master
module creator template updates
2020-05-19 09:07:25 -04:00
f0fe6551dd Merge pull request #24 from oqtane/master
sync
2020-05-19 08:24:12 -04:00
09b17e142e Merge pull request #503 from jimspillane/FixMergeFiles
Fix file upload merge
2020-05-19 07:52:07 -04:00
b8ce634f85 Fix file upload merge
Allow upload of file names that match the token pattern ".part_", but not in the file extension. For example, a file named, a.part_Y.txt, would not be uploaded.
2020-05-18 19:53:49 -04:00
1532eb7586 Optimized downloading of assemblies when using WebAssembly 2020-05-18 18:02:23 -04:00
9b65cd0e07 Merge pull request #502 from sbwalker/master
Optimized downloading of assemblies when using WebAssembly
2020-05-18 18:01:23 -04:00
b4596cb624 Merge pull request #11 from oqtane/master
Sync upstream
2020-05-18 15:30:43 -04:00
5e23448618 use lambas for setting resources 2020-05-18 13:34:53 -04:00
96c2a59551 Merge pull request #501 from sbwalker/master
use lambas for setting resources
2020-05-18 13:33:50 -04:00
6ae019336d changing default theme to Oqtane theme 2020-05-18 10:44:54 -04:00
0c236682b9 Merge pull request #500 from sbwalker/master
changing default theme to Oqtane theme
2020-05-18 10:44:00 -04:00
9b74262c76 Added support for module resource management 2020-05-18 09:47:37 -04:00
a13208e65d Merge pull request #499 from sbwalker/master
Added support for module resource management
2020-05-18 09:46:32 -04:00
f8ab886750 Fixed issue with loading resources 2020-05-16 22:11:58 -04:00
84b011224e Merge pull request #498 from sbwalker/master
Fixed issue with loading resources
2020-05-16 22:11:00 -04:00
c426302242 enhanced module creator to display location where module will be created 2020-05-16 13:40:59 -04:00
814e2100b2 Merge pull request #495 from sbwalker/master
enhanced module creator to display location where module will be created
2020-05-16 13:39:57 -04:00
54d4447d23 Central management of resources ( ie. stylesheets and scripts ) 2020-05-16 12:00:15 -04:00
93942d7cdd Merge pull request #492 from jimspillane/AddMissingSInCsproj
Fix module pluralization
2020-05-16 12:00:08 -04:00
0afd7d9ca4 Merge pull request #493 from sbwalker/master
Central management of resources ( ie. stylesheets and scripts )
2020-05-16 11:59:24 -04:00
3a19ced2d1 Fix module pluralization
Added 's' to the module creator sql script in the csproj to fix compilation error.
2020-05-16 10:36:16 -04:00
d5811a20e9 Merge pull request #10 from oqtane/master
Sync master
2020-05-16 10:25:28 -04:00
1b2600c6c4 Merge pull request #23 from oqtane/master
sync
2020-05-16 08:54:28 -04:00
f9cdc6d70c Merge pull request #490 from jimspillane/AddFileValidation
Add File Name validation
2020-05-16 08:53:39 -04:00
fbdf21320b Merge pull request #491 from chlupac/SettingsService
Setting service bug.
2020-05-16 08:52:13 -04:00
96f5668a3b Setting service bug. 2020-05-16 08:40:30 +02:00
13adebb36c Add File Name validation
Apply file name validation rules to the File Controller and client.
2020-05-15 23:12:24 -04:00
8a1e83ff7f Modified the package installer to use target folders ( based on the Nuget specification ) rather than file extensions 2020-05-15 17:43:45 -04:00
e698ea4d36 Merge pull request #489 from sbwalker/master
Modified the package installer to use target folders  ( based on the Nuget specification ) rather than file extensions
2020-05-15 17:42:49 -04:00
f5ce00ae7d Merge pull request #9 from oqtane/master
Sync upstream master
2020-05-15 12:56:45 -04:00
3cbb6e3e6e enable module creator to add embeddedresources to csproj for internal modules 2020-05-15 12:36:52 -04:00
9394e77fd5 Merge pull request #488 from sbwalker/master
enable module creator to add embeddedresources to csproj for internal modules
2020-05-15 12:35:52 -04:00
ac03afb146 added ability to set default container at the page level, expanded size of role description in upgrade script for 0.9.2 2020-05-15 09:50:48 -04:00
51c27ae0e5 Merge pull request #486 from sbwalker/master
added ability to set default container at the page level, expanded size of role description in upgrade script for 0.9.2
2020-05-15 09:49:45 -04:00
0ea4c4d723 Merge pull request #484 from jimspillane/MoveFolderValidationToShared
Move Path and File validation to Shared Utilities
2020-05-15 09:47:37 -04:00
b3dee737b4 Merge pull request #480 from chlupac/FileManager
FileManager Tune-Up
2020-05-15 09:46:28 -04:00
24ca9f4ded Merge pull request #485 from chlupac/FileController
File Controller bug
2020-05-15 07:28:12 -04:00
9850e249fc File Controller bug 2020-05-15 08:20:00 +02:00
5e04cb18a4 File Manager Tune-up 2020-05-15 08:18:07 +02:00
39641804f1 Move Path and File validation to Shared Utilities
Created extension methods:
IsPathValid(Folder)
IsFileValid(File)
IsPathOrFileValid(string)

Added client side validation check for Folders.
2020-05-14 22:02:57 -04:00
edc356292d Merge pull request #481 from thabaum/patch-6
container class added to pane
2020-05-14 21:34:09 -04:00
78f4af6b70 Merge pull request #482 from thabaum/patch-7
System Info input disabled changed to readonly
2020-05-14 18:41:36 -04:00
e98b8801f3 Merge pull request #483 from sbwalker/master
removed redundant assembly download logic, added security on download controller methods
2020-05-14 18:41:15 -04:00
caabac3e74 removed redundant assembly download logic, added security on download controller methods 2020-05-14 18:40:53 -04:00
422f360807 disabled changed to readonly 2020-05-14 14:58:09 -07:00
6e28fa47a2 container class added to pane
resolves issue with title border DIV not utilizing 100% pane size
2020-05-14 14:03:09 -07:00
b4f3c4ae56 Merge pull request #22 from oqtane/master
sync
2020-05-14 14:22:15 -04:00
bafe2c6666 fix module creator templates 2020-05-14 14:22:11 -04:00
5eec442442 Merge pull request #479 from sbwalker/master
fix module creator templates
2020-05-14 14:21:28 -04:00
def12489e6 Merge pull request #8 from oqtane/master
Sync master
2020-05-14 12:12:26 -04:00
82429c2545 Merge pull request #477 from jimspillane/PathTraversal
Add File and Path rules
2020-05-14 11:55:07 -04:00
aa97dd4d0d Allow modules to be installed/uninstalled with embedded scripts or IInstallable interface. Fix module uninstall issues. 2020-05-14 11:54:28 -04:00
cba5865e81 Merge pull request #478 from sbwalker/master
Allow modules to be installed/uninstalled with embedded scripts or IInstallable interface. Fix module uninstall issues.
2020-05-14 11:53:33 -04:00
8afe8e7474 Add File and Path rules
Apply the file and path naming rules found at 
https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file

Mitigate path traversal.
2020-05-14 09:53:36 -04:00
a9630e715b Merge pull request #7 from oqtane/master
Sync upstream master
2020-05-14 08:43:53 -04:00
6824b3f1b5 Merge pull request #471 from jimspillane/ReservedNameCheck
Add additional reserved names and characters
2020-05-13 16:25:27 -04:00
1cca18c4d2 Add additional reserved names and characters
Added CONIN$,CONOUT$ and characters <>:"/\|?*

Added .Split('.')[0] to folder.Name to catch names like CON.txt and allow names like CONTRACT.
2020-05-12 22:38:28 -04:00
a886ae12cc Merge pull request #6 from oqtane/master
Sync master
2020-05-12 20:52:07 -04:00
560c995564 Include AliasId in service API calls ( this is not needed for interacting with the MasterDB repository however it is needed for tenant-based logging ) 2020-05-12 20:31:31 -04:00
69a5077eda Merge pull request #470 from sbwalker/master
Include AliasId in service API calls ( this is not needed for interacting with the MasterDB repository however it is needed for tenant-based logging )
2020-05-12 20:30:27 -04:00
3efd39c74f Merge pull request #21 from oqtane/master
sync
2020-05-12 20:08:55 -04:00
bbcb054f7a Merge pull request #469 from chlupac/IClientStartup
Client assembly selection criteria changed
2020-05-12 20:07:27 -04:00
88cf30f7c6 Merge branch 'master' of https://github.com/oqtane/oqtane.framework into IClientStartup 2020-05-12 23:20:06 +02:00
4c188b782d ClientAssembly selection criteria changed 2020-05-12 23:17:37 +02:00
196e3d5865 Merge pull request #468 from sbwalker/master
updated module creator templates to use dependency injection in module components
2020-05-12 16:38:47 -04:00
6fd0efbb73 updated module creator templates to use dependency injection in module components 2020-05-12 16:31:53 -04:00
9eec0fd86b updated HtmlText module to use dependency injection 2020-05-12 15:04:07 -04:00
ad70128747 Merge pull request #466 from sbwalker/master
updated HtmlText module to use dependency injection
2020-05-12 15:04:01 -04:00
ee55b4e3cf Merge pull request #465 from sbwalker/master
add security to site template API
2020-05-12 14:38:52 -04:00
53f454e370 Merge pull request #20 from oqtane/master
sync
2020-05-12 14:37:37 -04:00
f05c7d79e3 add security to site template API 2020-05-12 14:31:18 -04:00
598b433cd2 Merge pull request #463 from chlupac/IClientStartup
IClientStartup  implementation
2020-05-12 14:31:10 -04:00
6ac4ba4617 Merge pull request #462 from mikecasas/patch-1
Update IModuleControl.cs
2020-05-12 14:30:59 -04:00
f4710f90c0 Merge pull request #464 from sbwalker/master
validate folder names, handle missing files more gracefully
2020-05-12 14:30:44 -04:00
6f3fe8d933 validate folder names, handle missing files more gracefully 2020-05-12 13:24:51 -04:00
da73d519d7 IClientStartup implementation 2020-05-12 10:00:28 +02:00
7f157582cc Update IModuleControl.cs
Added additional comments.
2020-05-11 13:47:12 -04:00
4d7ec16f36 Merge pull request #459 from chlupac/IServerStartup
IServerStartup implementation
2020-05-11 12:56:22 -04:00
7c814a67b3 IServerStartup implementation 2020-05-11 11:19:12 +02:00
c83496d814 Merge pull request #19 from oqtane/master
sync
2020-05-10 10:03:50 -04:00
83d47376cc Merge pull request #458 from jimspillane/FixContainsFiles
Fix contains files
2020-05-10 09:43:59 -04:00
a1449fb2dd Fix Uploadable files
When testing for allowable file extensions using a comma separated list, like (jpg,mp3,txt,zip), extensions such as .xt or .p3 will return true.  Adding Split(',') will test each of the extensions correctly.

Adding ToLower() will allow mixed case extensions, like .JPG or .Zip to return true.
2020-05-09 14:58:39 -04:00
2362faaee1 Merge pull request #5 from oqtane/master
Sync master
2020-05-09 14:04:36 -04:00
71f79bd90b Merge pull request #1 from oqtane/master
update master branch
2020-05-06 17:55:24 -07:00
7ca1f92f52 Merge pull request #4 from oqtane/master
Sync master
2020-05-05 21:26:18 -04:00
c30ee60433 Merge pull request #3 from oqtane/master
Sync master
2020-05-01 18:07:34 -04:00
e627e14233 Merge pull request #2 from oqtane/master
Sync master
2020-05-01 14:50:51 -04:00
4a6b12b6f9 Merge pull request #1 from oqtane/master
Sync master
2020-04-30 21:07:21 -04:00
216 changed files with 14733 additions and 1993 deletions

View File

@ -1,4 +1,5 @@
@namespace Oqtane.Modules.Admin.Files @namespace Oqtane.Modules.Admin.Files
@using System.IO
@inherits ModuleBase @inherits ModuleBase
@inject NavigationManager NavigationManager @inject NavigationManager NavigationManager
@inject IFileService FileService @inject IFileService FileService
@ -12,7 +13,7 @@
<Label For="upload" HelpText="Upload the file you want">Upload: </Label> <Label For="upload" HelpText="Upload the file you want">Upload: </Label>
</td> </td>
<td> <td>
<FileManager UploadMultiple="true" ShowFiles="false" FolderId="@_folderId.ToString()" /> <FileManager UploadMultiple="true" ShowFiles="false" FolderId="@_folderId" />
</td> </td>
</tr> </tr>
</table> </table>
@ -70,19 +71,33 @@
private async Task Download() private async Task Download()
{ {
try if (url == string.Empty || _folderId == -1)
{ {
if (url != string.Empty && _folderId != -1) AddModuleMessage("You Must Enter A Url And Select A Folder", MessageType.Warning);
return;
}
var filename = url.Substring(url.LastIndexOf("/", StringComparison.Ordinal) + 1);
if (!Constants.UploadableFiles.Split(',')
.Contains(Path.GetExtension(filename).ToLower().Replace(".", "")))
{
AddModuleMessage("File Could Not Be Downloaded From Url Due To Its File Extension", MessageType.Warning);
return ;
}
if (!filename.IsPathOrFileValid())
{
AddModuleMessage("You Must Enter A Url With A Valid File Name", MessageType.Warning);
return;
}
try
{ {
await FileService.UploadFileAsync(url, _folderId); await FileService.UploadFileAsync(url, _folderId);
await logger.LogInformation("File Downloaded Successfully From Url {Url}", url); await logger.LogInformation("File Downloaded Successfully From Url {Url}", url);
AddModuleMessage("File Downloaded Successfully From Url", MessageType.Success); AddModuleMessage("File Downloaded Successfully From Url", MessageType.Success);
} }
else
{
AddModuleMessage("You Must Enter A Url And Select A Folder", MessageType.Warning);
}
}
catch (Exception ex) catch (Exception ex)
{ {
await logger.LogError(ex, "Error Downloading File From Url {Url} {Error}", url, ex.Message); await logger.LogError(ex, "Error Downloading File From Url {Url} {Error}", url, ex.Message);

View File

@ -25,7 +25,7 @@
</tr> </tr>
<tr> <tr>
<td> <td>
<Label for="name" HelpText="Enter the file name">Name: </Label> <Label for="name" HelpText="Enter the folder name">Name: </Label>
</td> </td>
<td> <td>
<input id="name" class="form-control" @bind="@_name" /> <input id="name" class="form-control" @bind="@_name" />
@ -112,9 +112,19 @@
private async Task SaveFolder() private async Task SaveFolder()
{ {
try if (_name == string.Empty || _parentId == -1)
{ {
if (_name != string.Empty && _parentId != -1) AddModuleMessage("Folders Must Have A Parent And A Name", MessageType.Warning);
return;
}
if (!_name.IsPathOrFileValid())
{
AddModuleMessage("Folder Name Not Valid.", MessageType.Warning);
return;
}
try
{ {
Folder folder; Folder folder;
if (_folderId != -1) if (_folderId != -1)
@ -150,13 +160,15 @@
folder = await FolderService.AddFolderAsync(folder); folder = await FolderService.AddFolderAsync(folder);
} }
if (folder != null)
{
await FolderService.UpdateFolderOrderAsync(folder.SiteId, folder.FolderId, folder.ParentId); await FolderService.UpdateFolderOrderAsync(folder.SiteId, folder.FolderId, folder.ParentId);
await logger.LogInformation("Folder Saved {Folder}", folder); await logger.LogInformation("Folder Saved {Folder}", folder);
NavigationManager.NavigateTo(NavigateUrl()); NavigationManager.NavigateTo(NavigateUrl());
} }
else else
{ {
AddModuleMessage("Folders Must Have A Parent And A Name", MessageType.Warning); AddModuleMessage("An Error Was Encountered Saving The Folder", MessageType.Error);
} }
} }
catch (Exception ex) catch (Exception ex)

View File

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

View File

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

View File

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

View File

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

View File

@ -3,8 +3,9 @@
@inject NavigationManager NavigationManager @inject NavigationManager NavigationManager
@inject IModuleDefinitionService ModuleDefinitionService @inject IModuleDefinitionService ModuleDefinitionService
@inject IModuleService ModuleService @inject IModuleService ModuleService
@inject ISystemService SystemService
<table class="table table-borderless"> <table class="table table-borderless">
<tr> <tr>
<td> <td>
<Label For="owner" HelpText="Enter the name of the organization who is developing this module. It should not contain spaces or punctuation.">Owner Name: </Label> <Label For="owner" HelpText="Enter the name of the organization who is developing this module. It should not contain spaces or punctuation.">Owner Name: </Label>
@ -34,14 +35,25 @@
<Label For="template" HelpText="Select a module template. Internal modules are created inside of the Oqtane solution. External modules are created outside of the Oqtane solution.">Template: </Label> <Label For="template" HelpText="Select a module template. Internal modules are created inside of the Oqtane solution. External modules are created outside of the Oqtane solution.">Template: </Label>
</td> </td>
<td> <td>
<select id="template" class="form-control" @bind="@_template"> <select id="template" class="form-control" @onchange="(e => TemplateChanged(e))">
<option value="">&lt;Select Template&gt;</option> <option value="-">&lt;Select Template&gt;</option>
<option value="internal">Internal</option> <option value="internal">Internal</option>
<option value="external">External</option> <option value="external">External</option>
</select> </select>
</td> </td>
</tr> </tr>
</table> @if (!string.IsNullOrEmpty(_location))
{
<tr>
<td>
<Label For="location" HelpText="Location where the module will be created">Location: </Label>
</td>
<td>
<input id="module" class="form-control" @bind="@_location" readonly />
</td>
</tr>
}
</table>
<button type="button" class="btn btn-success" @onclick="CreateModule">Create Module</button> <button type="button" class="btn btn-success" @onclick="CreateModule">Create Module</button>
@ -49,7 +61,8 @@
private string _owner = string.Empty; private string _owner = string.Empty;
private string _module = string.Empty; private string _module = string.Empty;
private string _description = string.Empty; private string _description = string.Empty;
private string _template = string.Empty; private string _template = "-";
private string _location = string.Empty;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
@ -62,9 +75,9 @@
{ {
try try
{ {
if (!string.IsNullOrEmpty(_owner) && !string.IsNullOrEmpty(_module) && !string.IsNullOrEmpty(_template)) if (!string.IsNullOrEmpty(_owner) && !string.IsNullOrEmpty(_module) && _template != "-")
{ {
var moduleDefinition = new ModuleDefinition { Owner = _owner.Replace(" ",""), Name = _module.Replace(" ", ""), Description = _description, Template = _template }; var moduleDefinition = new ModuleDefinition { Owner = _owner.Replace(" ", ""), Name = _module.Replace(" ", ""), Description = _description, Template = _template };
await ModuleDefinitionService.CreateModuleDefinitionAsync(moduleDefinition, ModuleState.ModuleId); await ModuleDefinitionService.CreateModuleDefinitionAsync(moduleDefinition, ModuleState.ModuleId);
} }
else else
@ -77,4 +90,35 @@
await logger.LogError(ex, "Error Creating Module"); await logger.LogError(ex, "Error Creating Module");
} }
} }
private async void TemplateChanged(ChangeEventArgs e)
{
try
{
_location = string.Empty;
_template = (string)e.Value;
if (_template != "-")
{
Dictionary<string, string> systeminfo = await SystemService.GetSystemInfoAsync();
if (systeminfo != null)
{
string[] path = systeminfo["serverpath"].Split('\\');
if (_template == "internal")
{
_location = string.Join("\\", path, 0, path.Length - 1) + "\\Oqtane.Client\\Modules\\" + _owner + "." + _module + "s";
}
else
{
_location = string.Join("\\", path, 0, path.Length - 2) + "\\" + _owner + "." + _module + "s";
}
}
}
StateHasChanged();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Getting System Info {Error}", ex.Message);
AddModuleMessage("Error Getting System Info", MessageType.Error);
}
}
} }

View File

@ -16,7 +16,7 @@
<Header> <Header>
<th>Name</th> <th>Name</th>
<th>Version</th> <th>Version</th>
<th></th> <th style="width: 1px"></th>
</Header> </Header>
<Row> <Row>
<td>@context.Name</td> <td>@context.Name</td>
@ -35,7 +35,7 @@
<Label HelpText="Upload one or more module packages. Once they are uploaded click Install to complete the installation.">Module: </Label> <Label HelpText="Upload one or more module packages. Once they are uploaded click Install to complete the installation.">Module: </Label>
</td> </td>
<td> <td>
<FileManager Filter="nupkg" ShowFiles="false" Folder="Modules" UploadMultiple="True" /> <FileManager Filter="nupkg" ShowFiles="false" Folder="Modules" UploadMultiple="true" />
</td> </td>
</tr> </tr>
</table> </table>
@ -77,8 +77,10 @@
{ {
try try
{ {
ShowProgressIndicator();
var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 3);
await ModuleDefinitionService.InstallModuleDefinitionsAsync(); await ModuleDefinitionService.InstallModuleDefinitionsAsync();
NavigationManager.NavigateTo(NavigateUrl());
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@ -14,11 +14,11 @@ else
<Pager Items="@_moduleDefinitions"> <Pager Items="@_moduleDefinitions">
<Header> <Header>
<th>&nbsp;</th> <th style="width: 1px;">&nbsp;</th>
<th>&nbsp;</th> <th style="width: 1px;">&nbsp;</th>
<th>Name</th> <th>Name</th>
<th>Version</th> <th>Version</th>
<th>&nbsp;</th> <th style="width: 1px;">&nbsp;</th>
</Header> </Header>
<Row> <Row>
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.ModuleDefinitionId.ToString())" /></td> <td><ActionLink Action="Edit" Parameters="@($"id=" + context.ModuleDefinitionId.ToString())" /></td>
@ -54,21 +54,27 @@ else
_packages = await PackageService.GetPackagesAsync("module"); _packages = await PackageService.GetPackagesAsync("module");
} }
catch (Exception ex) catch (Exception ex)
{
if (_moduleDefinitions == null)
{ {
await logger.LogError(ex, "Error Loading Modules {Error}", ex.Message); await logger.LogError(ex, "Error Loading Modules {Error}", ex.Message);
AddModuleMessage("Error Loading Modules", MessageType.Error); AddModuleMessage("Error Loading Modules", MessageType.Error);
} }
} }
}
private bool UpgradeAvailable(string moduledefinitionname, string version) private bool UpgradeAvailable(string moduledefinitionname, string version)
{ {
var upgradeavailable = false; var upgradeavailable = false;
if (_packages != null)
{
var package = _packages.Where(item => item.PackageId == Utilities.GetTypeName(moduledefinitionname)).FirstOrDefault(); var package = _packages.Where(item => item.PackageId == Utilities.GetTypeName(moduledefinitionname)).FirstOrDefault();
if (package != null) if (package != null)
{ {
upgradeavailable = (Version.Parse(package.Version).CompareTo(Version.Parse(version)) > 0); upgradeavailable = (Version.Parse(package.Version).CompareTo(Version.Parse(version)) > 0);
} }
}
return upgradeavailable; return upgradeavailable;
} }
@ -77,9 +83,11 @@ else
try try
{ {
await PackageService.DownloadPackageAsync(moduledefinitionname, version, "Modules"); await PackageService.DownloadPackageAsync(moduledefinitionname, version, "Modules");
await ModuleDefinitionService.InstallModuleDefinitionsAsync();
await logger.LogInformation("Module Downloaded {ModuleDefinitionName} {Version}", moduledefinitionname, version); await logger.LogInformation("Module Downloaded {ModuleDefinitionName} {Version}", moduledefinitionname, version);
NavigationManager.NavigateTo(NavigateUrl()); ShowProgressIndicator();
var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 3);
await ModuleDefinitionService.InstallModuleDefinitionsAsync();
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -92,9 +100,10 @@ else
{ {
try try
{ {
ShowProgressIndicator();
var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 3);
await ModuleDefinitionService.DeleteModuleDefinitionAsync(moduleDefinition.ModuleDefinitionId, moduleDefinition.SiteId); await ModuleDefinitionService.DeleteModuleDefinitionAsync(moduleDefinition.ModuleDefinitionId, moduleDefinition.SiteId);
await logger.LogInformation("Module Deleted {ModuleDefinition}", moduleDefinition);
NavigationManager.NavigateTo(NavigateUrl());
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@ -24,10 +24,10 @@
</td> </td>
<td> <td>
<select id="container" class="form-control" @bind="@_containerType"> <select id="container" class="form-control" @bind="@_containerType">
<option value="">&lt;Select Container&gt;</option> <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> </select>
</td> </td>
@ -63,7 +63,7 @@
} }
</TabPanel> </TabPanel>
<TabPanel Name="Permissions"> <TabPanel Name="Permissions">
@if (_containers != null) @if (_permissions != null)
{ {
<table class="table table-borderless"> <table class="table table-borderless">
<tr> <tr>
@ -85,12 +85,12 @@
<NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink> <NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink>
@code { @code {
private Dictionary<string, string> _containers; private List<ThemeControl> _containers = new List<ThemeControl>();
private string _title; private string _title;
private string _containerType; private string _containerType;
private string _allPages = "false"; private string _allPages = "false";
private string _permissionNames = ""; private string _permissionNames = "";
private string _permissions; private string _permissions = null;
private string _pageId; private string _pageId;
private PermissionGrid _permissionGrid; private PermissionGrid _permissionGrid;
private Type _settingsModuleType; private Type _settingsModuleType;
@ -105,8 +105,16 @@
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
_title = ModuleState.Title; _title = ModuleState.Title;
_containers = ThemeService.GetContainerTypes(await ThemeService.GetThemesAsync()); _containers = ThemeService.GetContainerControls(await ThemeService.GetThemesAsync(), PageState.Page.ThemeType);
_containerType = ModuleState.ContainerType; _containerType = ModuleState.ContainerType;
if (!string.IsNullOrEmpty(PageState.Page.DefaultContainerType) && _containerType == PageState.Page.DefaultContainerType)
{
_containerType = "-";
}
if (_containerType == PageState.Site.DefaultContainerType)
{
_containerType = "-";
}
_allPages = ModuleState.AllPages.ToString(); _allPages = ModuleState.AllPages.ToString();
_permissions = ModuleState.Permissions; _permissions = ModuleState.Permissions;
_permissionNames = ModuleState.ModuleDefinition.PermissionNames; _permissionNames = ModuleState.ModuleDefinition.PermissionNames;
@ -115,8 +123,8 @@
_settingsModuleType = Type.GetType(ModuleState.ModuleType); _settingsModuleType = Type.GetType(ModuleState.ModuleType);
if (_settingsModuleType != null) if (_settingsModuleType != null)
{ {
var moduleobject = Activator.CreateInstance(_settingsModuleType); var moduleobject = Activator.CreateInstance(_settingsModuleType) as IModuleControl;
_settingstitle = (string)_settingsModuleType.GetProperty("Title").GetValue(moduleobject, null); _settingstitle = moduleobject.Title;
if (string.IsNullOrEmpty(_settingstitle)) if (string.IsNullOrEmpty(_settingstitle))
{ {
_settingstitle = "Other Settings"; _settingstitle = "Other Settings";
@ -136,7 +144,15 @@
var pagemodule = await PageModuleService.GetPageModuleAsync(ModuleState.PageModuleId); var pagemodule = await PageModuleService.GetPageModuleAsync(ModuleState.PageModuleId);
pagemodule.PageId = int.Parse(_pageId); pagemodule.PageId = int.Parse(_pageId);
pagemodule.Title = _title; pagemodule.Title = _title;
pagemodule.ContainerType = _containerType; pagemodule.ContainerType = (_containerType != "-") ? _containerType : string.Empty;
if (!string.IsNullOrEmpty(pagemodule.ContainerType) && pagemodule.ContainerType == PageState.Page.DefaultContainerType)
{
pagemodule.ContainerType = string.Empty;
}
if (!string.IsNullOrEmpty(pagemodule.ContainerType) && pagemodule.ContainerType == PageState.Site.DefaultContainerType)
{
pagemodule.ContainerType = string.Empty;
}
await PageModuleService.UpdatePageModuleAsync(pagemodule); await PageModuleService.UpdatePageModuleAsync(pagemodule);
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane); await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);

View File

@ -101,42 +101,59 @@
</td> </td>
<td> <td>
<select id="Theme" class="form-control" @onchange="(e => ThemeChanged(e))"> <select id="Theme" class="form-control" @onchange="(e => ThemeChanged(e))">
<option value="-">&lt;Select Theme&gt;</option> <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 else
{ {
<option value="@item.Key">@item.Value</option> <option value="@theme.TypeName">@theme.Name</option>
} }
} }
</select> </select>
</td> </td>
</tr> </tr>
@if (_layouts.Count > 0)
{
<tr> <tr>
<td> <td>
<Label For="Layout" HelpText="Select a layout for the page (if the selected theme supports it)">Layout: </Label> <Label For="Layout" HelpText="Select a layout for the page (if the selected theme supports it)">Layout: </Label>
</td> </td>
<td> <td>
<select id="Layout" class="form-control" @bind="@_layouttype"> <select id="Layout" class="form-control" @bind="@_layouttype">
<option value="-">&lt;Select Layout&gt;</option> <option value="-">&lt;Inherit From Site&gt;</option>
@foreach (KeyValuePair<string, string> panelayout in _panelayouts) @foreach (var layout in _layouts)
{ {
if (panelayout.Key == _layouttype) if (layout.TypeName == _layouttype)
{ {
<option value="@panelayout.Key" selected>@panelayout.Value</option> <option value="@(layout.TypeName)" selected>@(layout.Name)</option>
} }
else else
{ {
<option value="@panelayout.Key">@panelayout.Value</option> <option value="@(layout.TypeName)">@(layout.Name)</option>
} }
} }
</select> </select>
</td> </td>
</tr> </tr>
}
<tr>
<td>
<Label For="defaultContainer" HelpText="Select the default container for the page">Default Container: </Label>
</td>
<td>
<select id="defaultContainer" class="form-control" @bind="@_containertype">
<option value="-">&lt;Inherit From Site&gt;</option>
@foreach (var container in _containers)
{
<option value="@container.TypeName">@container.Name</option>
}
</select>
</td>
</tr>
<tr> <tr>
<td> <td>
<Label For="Icon" HelpText="Optionally provide an icon for this page which will be displayed in the site navigation">Icon: </Label> <Label For="Icon" HelpText="Optionally provide an icon for this page which will be displayed in the site navigation">Icon: </Label>
@ -185,9 +202,10 @@
<NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink> <NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink>
@code { @code {
private Dictionary<string, string> _themes;
private Dictionary<string, string> _panelayouts;
private List<Theme> _themeList; 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 List<Page> _pageList;
private string _name; private string _name;
private string _title; private string _title;
@ -202,6 +220,7 @@
private string _mode = "view"; private string _mode = "view";
private string _themetype = "-"; private string _themetype = "-";
private string _layouttype = "-"; private string _layouttype = "-";
private string _containertype = "-";
private string _icon = string.Empty; private string _icon = string.Empty;
private string _permissions = string.Empty; private string _permissions = string.Empty;
private PermissionGrid _permissionGrid; private PermissionGrid _permissionGrid;
@ -216,12 +235,7 @@
_pageList = PageState.Pages; _pageList = PageState.Pages;
_children = PageState.Pages.Where(item => item.ParentId == null).ToList(); _children = PageState.Pages.Where(item => item.ParentId == null).ToList();
_themetype = PageState.Site.DefaultThemeType; _themes = ThemeService.GetThemeControls(_themeList);
_layouttype = PageState.Site.DefaultLayoutType;
_themes = ThemeService.GetThemeTypes(_themeList);
_panelayouts = ThemeService.GetPaneLayoutTypes(_themeList, _themetype);
_permissions = string.Empty; _permissions = string.Empty;
} }
catch (Exception ex) catch (Exception ex)
@ -273,12 +287,16 @@
_themetype = (string)e.Value; _themetype = (string)e.Value;
if (_themetype != "-") if (_themetype != "-")
{ {
_panelayouts = ThemeService.GetPaneLayoutTypes(_themeList, _themetype); _layouts = ThemeService.GetLayoutControls(_themeList, _themetype);
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
} }
else else
{ {
_panelayouts = new Dictionary<string, string>(); _layouts = new List<ThemeControl>();
_containers = new List<ThemeControl>();
} }
_layouttype = "-";
_containertype = "-";
StateHasChanged(); StateHasChanged();
} }
catch (Exception ex) catch (Exception ex)
@ -293,7 +311,7 @@
Page page = null; Page page = null;
try 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 = new Page();
page.SiteId = PageState.Page.SiteId; page.SiteId = PageState.Page.SiteId;
@ -351,16 +369,20 @@
page.Url = _url; page.Url = _url;
page.EditMode = (_mode == "edit" ? true : false); page.EditMode = (_mode == "edit" ? true : false);
page.ThemeType = (_themetype != "-") ? _themetype : string.Empty; page.ThemeType = (_themetype != "-") ? _themetype : string.Empty;
page.LayoutType = (_layouttype != "-") ? _layouttype : string.Empty; if (!string.IsNullOrEmpty(page.ThemeType) && page.ThemeType == PageState.Site.DefaultThemeType)
if (page.ThemeType == PageState.Site.DefaultThemeType)
{ {
page.ThemeType = string.Empty; page.ThemeType = string.Empty;
} }
page.LayoutType = (_layouttype != "-") ? _layouttype : string.Empty;
if (page.LayoutType == PageState.Site.DefaultLayoutType) if (!string.IsNullOrEmpty(page.LayoutType) && page.LayoutType == PageState.Site.DefaultLayoutType)
{ {
page.LayoutType = string.Empty; page.LayoutType = string.Empty;
} }
page.DefaultContainerType = (_containertype != "-") ? _containertype : string.Empty;
if (!string.IsNullOrEmpty(page.DefaultContainerType) && page.DefaultContainerType == PageState.Site.DefaultContainerType)
{
page.DefaultContainerType = string.Empty;
}
page.Icon = (_icon == null ? string.Empty : _icon); page.Icon = (_icon == null ? string.Empty : _icon);
page.Permissions = _permissionGrid.GetPermissions(); page.Permissions = _permissionGrid.GetPermissions();
page.IsPersonalizable = (_ispersonalizable == null ? false : Boolean.Parse(_ispersonalizable)); page.IsPersonalizable = (_ispersonalizable == null ? false : Boolean.Parse(_ispersonalizable));

View File

@ -112,42 +112,59 @@
</td> </td>
<td> <td>
<select id="Theme" class="form-control" @onchange="(e => ThemeChanged(e))"> <select id="Theme" class="form-control" @onchange="(e => ThemeChanged(e))">
<option value="-">&lt;Select Theme&gt;</option> <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 else
{ {
<option value="@item.Key">@item.Value</option> <option value="@theme.TypeName">@theme.Name</option>
} }
} }
</select> </select>
</td> </td>
</tr> </tr>
@if (_layouts.Count > 0)
{
<tr> <tr>
<td> <td>
<Label For="Layout" HelpText="Select a layout for the page (if the selected theme supports it)">Layout: </Label> <Label For="Layout" HelpText="Select a layout for the page (if the selected theme supports it)">Layout: </Label>
</td> </td>
<td> <td>
<select id="Layout" class="form-control" @bind="@_layouttype"> <select id="Layout" class="form-control" @bind="@_layouttype">
<option value="-">&lt;Select Layout&gt;</option> <option value="-">&lt;Inherit From Site&gt;</option>
@foreach (KeyValuePair<string, string> panelayout in _panelayouts) @foreach (var layout in _layouts)
{ {
if (panelayout.Key == _layouttype) if (layout.TypeName == _layouttype)
{ {
<option value="@panelayout.Key" selected>@panelayout.Value</option> <option value="@(layout.TypeName)" selected>@(layout.Name)</option>
} }
else else
{ {
<option value="@panelayout.Key">@panelayout.Value</option> <option value="@(layout.TypeName)">@(layout.Name)</option>
} }
} }
</select> </select>
</td> </td>
</tr> </tr>
}
<tr>
<td>
<Label For="defaultContainer" HelpText="Select the default container for the page">Default Container: </Label>
</td>
<td>
<select id="defaultContainer" class="form-control" @bind="@_containertype">
<option value="-">&lt;Inherit From Site&gt;</option>
@foreach (var container in _containers)
{
<option value="@container.TypeName">@container.Name</option>
}
</select>
</td>
</tr>
<tr> <tr>
<td> <td>
<Label For="Icon" HelpText="Optionally provide an icon for this page which will be displayed in the site navigation">Icon: </Label> <Label For="Icon" HelpText="Optionally provide an icon for this page which will be displayed in the site navigation">Icon: </Label>
@ -185,6 +202,8 @@
} }
</TabPanel> </TabPanel>
<TabPanel Name="Permissions"> <TabPanel Name="Permissions">
@if (_permissions != null)
{
<table class="table table-borderless"> <table class="table table-borderless">
<tr> <tr>
<td> <td>
@ -192,15 +211,17 @@
</td> </td>
</tr> </tr>
</table> </table>
}
</TabPanel> </TabPanel>
</TabStrip> </TabStrip>
<button type="button" class="btn btn-success" @onclick="SavePage">Save</button> <button type="button" class="btn btn-success" @onclick="SavePage">Save</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink> <NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink>
@code { @code {
private Dictionary<string, string> _themes;
private Dictionary<string, string> _panelayouts;
private List<Theme> _themeList; 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 List<Page> _pageList;
private int _pageId; private int _pageId;
private string _name; private string _name;
@ -217,8 +238,9 @@
private string _mode; private string _mode;
private string _themetype = "-"; private string _themetype = "-";
private string _layouttype = "-"; private string _layouttype = "-";
private string _containertype = "-";
private string _icon; private string _icon;
private string _permissions; private string _permissions = null;
private string _createdby; private string _createdby;
private DateTime _createdon; private DateTime _createdon;
private string _modifiedby; private string _modifiedby;
@ -236,11 +258,11 @@
{ {
try try
{ {
_themeList = await ThemeService.GetThemesAsync();
_pageList = PageState.Pages; _pageList = PageState.Pages;
_children = PageState.Pages.Where(item => item.ParentId == null).ToList(); _children = PageState.Pages.Where(item => item.ParentId == null).ToList();
_themes = ThemeService.GetThemeTypes(_themeList); _themeList = await ThemeService.GetThemesAsync();
_themes = ThemeService.GetThemeControls(_themeList);
_pageId = Int32.Parse(PageState.QueryString["id"]); _pageId = Int32.Parse(PageState.QueryString["id"]);
var page = PageState.Pages.FirstOrDefault(item => item.PageId == _pageId); var page = PageState.Pages.FirstOrDefault(item => item.PageId == _pageId);
@ -270,16 +292,22 @@
_ispersonalizable = page.IsPersonalizable.ToString(); _ispersonalizable = page.IsPersonalizable.ToString();
_mode = (page.EditMode) ? "edit" : "view"; _mode = (page.EditMode) ? "edit" : "view";
_themetype = page.ThemeType; _themetype = page.ThemeType;
_panelayouts = ThemeService.GetPaneLayoutTypes(_themeList, _themetype);
_layouttype = page.LayoutType;
if (_themetype == PageState.Site.DefaultThemeType) if (_themetype == PageState.Site.DefaultThemeType)
{ {
_themetype = "-"; _themetype = "-";
} }
_layouts = ThemeService.GetLayoutControls(_themeList, page.ThemeType);
_layouttype = page.LayoutType;
if (_layouttype == PageState.Site.DefaultLayoutType) if (_layouttype == PageState.Site.DefaultLayoutType)
{ {
_layouttype = "-"; _layouttype = "-";
} }
_containers = ThemeService.GetContainerControls(_themeList, page.ThemeType);
_containertype = page.DefaultContainerType;
if (string.IsNullOrEmpty(_containertype))
{
_containertype = "-";
}
_icon = page.Icon; _icon = page.Icon;
_permissions = page.Permissions; _permissions = page.Permissions;
_createdby = page.CreatedBy; _createdby = page.CreatedBy;
@ -347,12 +375,16 @@
_themetype = (string)e.Value; _themetype = (string)e.Value;
if (_themetype != "-") if (_themetype != "-")
{ {
_panelayouts = ThemeService.GetPaneLayoutTypes(_themeList, _themetype); _layouts = ThemeService.GetLayoutControls(_themeList, _themetype);
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
} }
else else
{ {
_panelayouts = new Dictionary<string, string>(); _layouts = new List<ThemeControl>();
_containers = new List<ThemeControl>();
} }
_layouttype = "-";
_containertype = "-";
StateHasChanged(); StateHasChanged();
} }
catch (Exception ex) catch (Exception ex)
@ -426,15 +458,20 @@
page.Url = _url; page.Url = _url;
page.EditMode = (_mode == "edit"); page.EditMode = (_mode == "edit");
page.ThemeType = (_themetype != "-") ? _themetype : string.Empty; page.ThemeType = (_themetype != "-") ? _themetype : string.Empty;
page.LayoutType = (_layouttype != "-") ? _layouttype : string.Empty; if (!string.IsNullOrEmpty(page.ThemeType) && page.ThemeType == PageState.Site.DefaultThemeType)
if (page.ThemeType == PageState.Site.DefaultThemeType)
{ {
page.ThemeType = string.Empty; page.ThemeType = string.Empty;
} }
if (page.LayoutType == PageState.Site.DefaultLayoutType) page.LayoutType = (_layouttype != "-") ? _layouttype : string.Empty;
if (!string.IsNullOrEmpty(page.LayoutType) && page.LayoutType == PageState.Site.DefaultLayoutType)
{ {
page.LayoutType = string.Empty; page.LayoutType = string.Empty;
} }
page.DefaultContainerType = (_containertype != "-") ? _containertype : string.Empty;
if (!string.IsNullOrEmpty(page.DefaultContainerType) && page.DefaultContainerType == PageState.Site.DefaultContainerType)
{
page.DefaultContainerType = string.Empty;
}
page.Icon = _icon ?? string.Empty; page.Icon = _icon ?? string.Empty;
page.Permissions = _permissionGrid.GetPermissions(); page.Permissions = _permissionGrid.GetPermissions();
page.IsPersonalizable = (_ispersonalizable != null && Boolean.Parse(_ispersonalizable)); page.IsPersonalizable = (_ispersonalizable != null && Boolean.Parse(_ispersonalizable));

View File

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

View File

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

View File

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

View File

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

View File

@ -39,7 +39,7 @@
<Label For="logo" HelpText="Upload a logo for the site">Logo: </Label> <Label For="logo" HelpText="Upload a logo for the site">Logo: </Label>
</td> </td>
<td> <td>
<FileManager FileId="@_logofileid.ToString()" Filter="@Constants.ImageFiles" @ref="_logofilemanager" /> <FileManager FileId="@_logofileid" Filter="@Constants.ImageFiles" @ref="_logofilemanager" />
</td> </td>
</tr> </tr>
<tr> <tr>
@ -47,7 +47,7 @@
<Label For="favicon" HelpText="Select Your default icon">Favicon: </Label> <Label For="favicon" HelpText="Select Your default icon">Favicon: </Label>
</td> </td>
<td> <td>
<FileManager FileId="@_faviconfileid.ToString()" Filter="ico" @ref="_faviconfilemanager" /> <FileManager FileId="@_faviconfileid" Filter="ico" @ref="_faviconfilemanager" />
</td> </td>
</tr> </tr>
<tr> <tr>
@ -56,45 +56,48 @@
</td> </td>
<td> <td>
<select id="defaultTheme" class="form-control" @onchange="(e => ThemeChanged(e))"> <select id="defaultTheme" class="form-control" @onchange="(e => ThemeChanged(e))">
<option value="">&lt;Select Theme&gt;</option> <option value="-">&lt;Select Theme&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 else
{ {
<option value="@item.Key">@item.Value</option> <option value="@theme.TypeName">@theme.Name</option>
} }
} }
</select> </select>
</td> </td>
</tr> </tr>
@if (_layouts.Count > 0)
{
<tr> <tr>
<td> <td>
<Label For="defaultLayout" HelpText="Select the sites default layout">Default Layout: </Label> <Label For="defaultLayout" HelpText="Select the sites default layout">Default Layout: </Label>
</td> </td>
<td> <td>
<select id="defaultLayout" class="form-control" @bind="@_layouttype"> <select id="defaultLayout" class="form-control" @bind="@_layouttype">
<option value="">&lt;Select Layout&gt;</option> <option value="-">&lt;Select Layout&gt;</option>
@foreach (KeyValuePair<string, string> panelayout in _panelayouts) @foreach (var layout in _layouts)
{ {
<option value="@panelayout.Key">@panelayout.Value</option> <option value="@(layout.TypeName)">@(layout.Name)</option>
} }
</select> </select>
</td> </td>
</tr> </tr>
}
<tr> <tr>
<td> <td>
<Label For="defaultContainer" HelpText="Select the default container for the site">Default Container: </Label> <Label For="defaultContainer" HelpText="Select the default container for the site">Default Container: </Label>
</td> </td>
<td> <td>
<select id="defaultContainer" class="form-control" @bind="@_containertype"> <select id="defaultContainer" class="form-control" @bind="@_containertype">
<option value="">&lt;Select Container&gt;</option> <option value="-">&lt;Select Container&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> </select>
</td> </td>
@ -185,7 +188,7 @@
<Label For="appIcon" HelpText="Include an application icon for your PWA. It should be a PNG which is 192 X 192 pixels in dimension.">App Icon: </Label> <Label For="appIcon" HelpText="Include an application icon for your PWA. It should be a PNG which is 192 X 192 pixels in dimension.">App Icon: </Label>
</td> </td>
<td> <td>
<FileManager FileId="@_pwaappiconfileid.ToString()" Filter="png" @ref="_pwaappiconfilemanager" /> <FileManager FileId="@_pwaappiconfileid" Filter="png" @ref="_pwaappiconfilemanager" />
</td> </td>
</tr> </tr>
<tr> <tr>
@ -193,7 +196,7 @@
<Label For="splashIcon" HelpText="Include a splash icon for your PWA. It should be a PNG which is 512 X 512 pixels in dimension.">Splash Icon: </Label> <Label For="splashIcon" HelpText="Include a splash icon for your PWA. It should be a PNG which is 512 X 512 pixels in dimension.">Splash Icon: </Label>
</td> </td>
<td> <td>
<FileManager FileId="@_pwasplashiconfileid.ToString()" Filter="png" @ref="_pwasplashiconfilemanager" /> <FileManager FileId="@_pwasplashiconfileid" Filter="png" @ref="_pwasplashiconfilemanager" />
</td> </td>
</tr> </tr>
</table> </table>
@ -208,10 +211,10 @@
} }
@code { @code {
private Dictionary<string, string> _themes;
private Dictionary<string, string> _panelayouts;
private Dictionary<string, string> _containers;
private List<Theme> _themeList; 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 string _name = string.Empty;
private List<Tenant> _tenantList; private List<Tenant> _tenantList;
private string _tenant = string.Empty; private string _tenant = string.Empty;
@ -221,9 +224,9 @@
private FileManager _logofilemanager; private FileManager _logofilemanager;
private int _faviconfileid = -1; private int _faviconfileid = -1;
private FileManager _faviconfilemanager; private FileManager _faviconfilemanager;
private string _themetype; private string _themetype = "-";
private string _layouttype; private string _layouttype = "-";
private string _containertype; private string _containertype = "-";
private string _allowregistration; private string _allowregistration;
private string _smtphost = string.Empty; private string _smtphost = string.Empty;
private string _smtpport = string.Empty; private string _smtpport = string.Empty;
@ -271,9 +274,11 @@
_faviconfileid = site.FaviconFileId.Value; _faviconfileid = site.FaviconFileId.Value;
} }
_themes = ThemeService.GetThemeControls(_themeList);
_themetype = site.DefaultThemeType; _themetype = site.DefaultThemeType;
_panelayouts = ThemeService.GetPaneLayoutTypes(_themeList, _themetype); _layouts = ThemeService.GetLayoutControls(_themeList, _themetype);
_layouttype = site.DefaultLayoutType; _layouttype = site.DefaultLayoutType;
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
_containertype = site.DefaultContainerType; _containertype = site.DefaultContainerType;
_allowregistration = site.AllowRegistration.ToString(); _allowregistration = site.AllowRegistration.ToString();
@ -314,9 +319,6 @@
_deletedon = site.DeletedOn; _deletedon = site.DeletedOn;
_isdeleted = site.IsDeleted.ToString(); _isdeleted = site.IsDeleted.ToString();
} }
_themes = ThemeService.GetThemeTypes(_themeList);
_containers = ThemeService.GetContainerTypes(_themeList);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -330,14 +332,18 @@
try try
{ {
_themetype = (string)e.Value; _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 else
{ {
_panelayouts = new Dictionary<string, string>(); _layouts = new List<ThemeControl>();
_containers = new List<ThemeControl>();
} }
_layouttype = "-";
_containertype = "-";
StateHasChanged(); StateHasChanged();
} }
catch (Exception ex) catch (Exception ex)
@ -351,7 +357,7 @@
{ {
try 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; var unique = true;
foreach (string name in _urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) foreach (string name in _urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
@ -376,7 +382,7 @@
} }
site.DefaultThemeType = _themetype; site.DefaultThemeType = _themetype;
site.DefaultLayoutType = (_layouttype == null ? string.Empty : _layouttype); site.DefaultLayoutType = (_layouttype == "-" ? string.Empty : _layouttype);
site.DefaultContainerType = _containertype; site.DefaultContainerType = _containertype;
site.AllowRegistration = (_allowregistration == null ? true : Boolean.Parse(_allowregistration)); site.AllowRegistration = (_allowregistration == null ? true : Boolean.Parse(_allowregistration));
site.IsDeleted = (_isdeleted == null ? true : Boolean.Parse(_isdeleted)); site.IsDeleted = (_isdeleted == null ? true : Boolean.Parse(_isdeleted));

View File

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

View File

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

View File

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

View File

@ -8,7 +8,7 @@
<Label For="version" HelpText="Framework Version">Framework Version: </Label> <Label For="version" HelpText="Framework Version">Framework Version: </Label>
</td> </td>
<td> <td>
<input id="version" class="form-control" @bind="@_version" disabled /> <input id="version" class="form-control" @bind="@_version" readonly />
</td> </td>
</tr> </tr>
<tr> <tr>
@ -16,7 +16,7 @@
<Label For="runtime" HelpText="Blazor Runtime (Server or WebAssembly)">Blazor Runtime: </Label> <Label For="runtime" HelpText="Blazor Runtime (Server or WebAssembly)">Blazor Runtime: </Label>
</td> </td>
<td> <td>
<input id="runtime" class="form-control" @bind="@_runtime" disabled /> <input id="runtime" class="form-control" @bind="@_runtime" readonly />
</td> </td>
</tr> </tr>
<tr> <tr>
@ -24,7 +24,7 @@
<Label For="clrversion" HelpText="Common Language Runtime Version">CLR Version: </Label> <Label For="clrversion" HelpText="Common Language Runtime Version">CLR Version: </Label>
</td> </td>
<td> <td>
<input id="clrversion" class="form-control" @bind="@_clrversion" disabled /> <input id="clrversion" class="form-control" @bind="@_clrversion" readonly />
</td> </td>
</tr> </tr>
<tr> <tr>
@ -32,7 +32,7 @@
<Label For="osversion" HelpText="Operating System Version">OS Version: </Label> <Label For="osversion" HelpText="Operating System Version">OS Version: </Label>
</td> </td>
<td> <td>
<input id="osversion" class="form-control" @bind="@_osversion" disabled /> <input id="osversion" class="form-control" @bind="@_osversion" readonly />
</td> </td>
</tr> </tr>
<tr> <tr>
@ -40,7 +40,7 @@
<Label For="serverpath" HelpText="Server Path">Server Path: </Label> <Label For="serverpath" HelpText="Server Path">Server Path: </Label>
</td> </td>
<td> <td>
<input id="serverpath" class="form-control" @bind="@_serverpath" disabled /> <input id="serverpath" class="form-control" @bind="@_serverpath" readonly />
</td> </td>
</tr> </tr>
<tr> <tr>
@ -48,7 +48,7 @@
<Label For="servertime" HelpText="Server Time">Server Time: </Label> <Label For="servertime" HelpText="Server Time">Server Time: </Label>
</td> </td>
<td> <td>
<input id="servertime" class="form-control" @bind="@_servertime" disabled /> <input id="servertime" class="form-control" @bind="@_servertime" readonly />
</td> </td>
</tr> </tr>
</table> </table>

View File

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

View File

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

View File

@ -1,10 +1,11 @@
@namespace Oqtane.Modules.Admin.Themes @namespace Oqtane.Modules.Admin.Themes
@using System.Net
@inherits ModuleBase @inherits ModuleBase
@inject NavigationManager NavigationManager @inject NavigationManager NavigationManager
@inject IThemeService ThemeService @inject IThemeService ThemeService
@inject IPackageService PackageService @inject IPackageService PackageService
@if (themes == null) @if (_themes == null)
{ {
<p><em>Loading...</em></p> <p><em>Loading...</em></p>
} }
@ -12,14 +13,16 @@ else
{ {
<ActionLink Action="Add" Text="Install Theme" /> <ActionLink Action="Add" Text="Install Theme" />
<Pager Items="@themes"> <Pager Items="@_themes">
<Header> <Header>
<th>&nbsp;</th> <th style="width: 1px;">&nbsp;</th>
<th>Name</th> <th style="width: 1px;">&nbsp;</th>
<th>Version</th> <th scope="col">Name</th>
<th scope="col">Version</th>
<th>&nbsp;</th> <th>&nbsp;</th>
</Header> </Header>
<Row> <Row>
<td><ActionLink Action="View" Parameters="@($"name=" + WebUtility.UrlEncode(context.ThemeName))" /></td>
<td> <td>
@if (context.AssemblyName != "Oqtane.Client") @if (context.AssemblyName != "Oqtane.Client")
{ {
@ -34,46 +37,79 @@ else
<button type="button" class="btn btn-success" @onclick=@(async () => await DownloadTheme(context.ThemeName, context.Version))>Upgrade</button> <button type="button" class="btn btn-success" @onclick=@(async () => await DownloadTheme(context.ThemeName, context.Version))>Upgrade</button>
} }
</td> </td>
<td></td>
</Row> </Row>
</Pager> </Pager>
} }
@code { @code {
private List<Theme> themes; private List<Theme> _themes;
private List<Package> packages; private List<Package> _packages;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
themes = await ThemeService.GetThemesAsync(); try
packages = await PackageService.GetPackagesAsync("module"); {
_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) private bool UpgradeAvailable(string themename, string version)
{ {
var upgradeavailable = false; var upgradeavailable = false;
var package = packages.Where(item => item.PackageId == Utilities.GetTypeName(themename)).FirstOrDefault(); if (_packages != null)
{
var package = _packages.Where(item => item.PackageId == Utilities.GetTypeName(themename)).FirstOrDefault();
if (package != null) if (package != null)
{ {
upgradeavailable = (Version.Parse(package.Version).CompareTo(Version.Parse(version)) > 0); upgradeavailable = (Version.Parse(package.Version).CompareTo(Version.Parse(version)) > 0);
} }
}
return upgradeavailable; return upgradeavailable;
} }
private async Task DownloadTheme(string themename, string version) private async Task DownloadTheme(string themename, string version)
{
try
{ {
await PackageService.DownloadPackageAsync(themename, version, "Themes"); await PackageService.DownloadPackageAsync(themename, version, "Themes");
await logger.LogInformation("Theme Downloaded {ThemeName} {Version}", themename, version); await logger.LogInformation("Theme Downloaded {ThemeName} {Version}", themename, version);
ShowProgressIndicator();
var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 3);
await ThemeService.InstallThemesAsync(); await ThemeService.InstallThemesAsync();
NavigationManager.NavigateTo(NavigateUrl()); }
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) private async Task DeleteTheme(Theme Theme)
{ {
try
{
ShowProgressIndicator();
var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 3);
await ThemeService.DeleteThemeAsync(Theme.ThemeName); await ThemeService.DeleteThemeAsync(Theme.ThemeName);
await logger.LogInformation("Theme Deleted {Theme}", Theme); }
NavigationManager.NavigateTo(NavigateUrl()); 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,7 +7,7 @@
@if (_package != null) @if (_package != null)
{ {
<TabStrip> <TabStrip>
<TabPanel Name="Download"> <TabPanel Name="Download">
@if (_upgradeavailable) @if (_upgradeavailable)
{ {
@ -26,13 +26,13 @@
<Label HelpText="Upload a framework package and select Install to complete the installation">Framework: </Label> <Label HelpText="Upload a framework package and select Install to complete the installation">Framework: </Label>
</td> </td>
<td> <td>
<FileManager Filter="nupkg" ShowFiles="false" Folder="Framework" /> <FileManager Filter="nupkg" Folder="Framework" />
</td> </td>
</tr> </tr>
</table> </table>
<button type="button" class="btn btn-success" @onclick="Upgrade">Install</button> <button type="button" class="btn btn-success" @onclick="Upgrade">Install</button>
</TabPanel> </TabPanel>
</TabStrip> </TabStrip>
} }
@code { @code {
@ -42,8 +42,12 @@
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{
try
{ {
List<Package> packages = await PackageService.GetPackagesAsync("framework"); List<Package> packages = await PackageService.GetPackagesAsync("framework");
if (packages != null)
{
_package = packages.FirstOrDefault(); _package = packages.FirstOrDefault();
if (_package != null) if (_package != null)
{ {
@ -54,17 +58,43 @@
_package = new Package { Name = Constants.PackageId, Version = Constants.Version }; _package = new Package { Name = Constants.PackageId, Version = Constants.Version };
} }
} }
}
catch
{
// can be caused by no network connection
}
}
private async Task Upgrade() private async Task Upgrade()
{ {
try
{
ShowProgressIndicator();
var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 3);
await InstallationService.Upgrade(); await InstallationService.Upgrade();
NavigationManager.NavigateTo(NavigateUrl()); }
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) private async Task Download(string packageid, string version)
{
try
{ {
await PackageService.DownloadPackageAsync(packageid, version, "Framework"); await PackageService.DownloadPackageAsync(packageid, version, "Framework");
ShowProgressIndicator();
var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 3);
await InstallationService.Upgrade(); await InstallationService.Upgrade();
NavigationManager.NavigateTo(NavigateUrl()); }
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 @namespace Oqtane.Modules.Admin.UserProfile
@inherits ModuleBase @inherits ModuleBase
@inject NavigationManager NavigationManager @inject NavigationManager NavigationManager
@inject IUserRoleService UserRoleService @inject IUserService UserService
@inject INotificationService NotificationService @inject INotificationService NotificationService
@if (PageState.User != null) @if (PageState.User != null)
@ -9,19 +9,10 @@
<table class="table table-borderless"> <table class="table table-borderless">
<tr> <tr>
<td> <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>
<td> <td>
<select id="to" class="form-control" @bind="@userid"> <input id="to" class="form-control" @bind="@username" />
<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>
</td> </td>
</tr> </tr>
<tr> <tr>
@ -46,8 +37,7 @@
} }
@code { @code {
private List<UserRole> userroles; private string username = "";
private string userid = "-1";
private string subject = ""; private string subject = "";
private string body = ""; private string body = "";
@ -55,42 +45,37 @@
public override string Title => "Send Notification"; 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() private async Task Send()
{ {
var notification = new Notification(); var notification = new Notification();
try try
{
var user = await UserService.GetUserAsync(username, PageState.Site.SiteId);
if (user != null)
{ {
notification.SiteId = PageState.Site.SiteId; notification.SiteId = PageState.Site.SiteId;
notification.FromUserId = PageState.User.UserId; notification.FromUserId = PageState.User.UserId;
notification.ToUserId = int.Parse(userid); notification.FromDisplayName = PageState.User.DisplayName;
notification.ToEmail = ""; notification.FromEmail = PageState.User.Email;
notification.ToUserId = user.UserId;
notification.ToDisplayName = user.DisplayName;
notification.ToEmail = user.Email;
notification.Subject = subject; notification.Subject = subject;
notification.Body = body; notification.Body = body;
notification.ParentId = null; notification.ParentId = null;
notification.CreatedOn = DateTime.UtcNow; notification.CreatedOn = DateTime.UtcNow;
notification.IsDelivered = false; notification.IsDelivered = false;
notification.DeliveredOn = null; notification.DeliveredOn = null;
notification.SendOn = DateTime.UtcNow;
notification = await NotificationService.AddNotificationAsync(notification); notification = await NotificationService.AddNotificationAsync(notification);
await logger.LogInformation("Notification Created {Notification}", notification); await logger.LogInformation("Notification Created {Notification}", notification);
NavigationManager.NavigateTo(NavigateUrl()); NavigationManager.NavigateTo(NavigateUrl());
} }
else
{
AddModuleMessage("User Does Not Exist. Please Verify That The Username Provided Is Correct.", MessageType.Warning);
}
}
catch (Exception ex) catch (Exception ex)
{ {
await logger.LogError(ex, "Error Adding Notification {Notification} {Error}", notification, ex.Message); await logger.LogError(ex, "Error Adding Notification {Notification} {Error}", notification, ex.Message);

View File

@ -32,7 +32,7 @@ else
<label for="Name" class="control-label">Password: </label> <label for="Name" class="control-label">Password: </label>
</td> </td>
<td> <td>
<input type="password" class="form-control" @bind="@password" /> <input type="password" class="form-control" @bind="@password" autocomplete="new-password" />
</td> </td>
</tr> </tr>
<tr> <tr>
@ -40,7 +40,7 @@ else
<label for="Name" class="control-label">Confirm Password: </label> <label for="Name" class="control-label">Confirm Password: </label>
</td> </td>
<td> <td>
<input type="password" class="form-control" @bind="@confirm" /> <input type="password" class="form-control" @bind="@confirm" autocomplete="new-password" />
</td> </td>
</tr> </tr>
<tr> <tr>
@ -64,7 +64,7 @@ else
<label for="Name" class="control-label">Photo: </label> <label for="Name" class="control-label">Photo: </label>
</td> </td>
<td> <td>
<FileManager FileId="@photofileid.ToString()" @ref="filemanager" /> <FileManager FileId="@photofileid" @ref="filemanager" />
</td> </td>
</tr> </tr>
</table> </table>
@ -73,7 +73,7 @@ else
} }
</TabPanel> </TabPanel>
<TabPanel Name="Profile"> <TabPanel Name="Profile">
@if (profiles != null) @if (profiles != null && settings != null)
{ {
<table class="table table-borderless"> <table class="table table-borderless">
@foreach (Profile profile in profiles) @foreach (Profile profile in profiles)
@ -111,8 +111,8 @@ else
{ {
<Pager Items="@notifications"> <Pager Items="@notifications">
<Header> <Header>
<th>&nbsp;</th> <th style="width: 1px;">&nbsp;</th>
<th>&nbsp;</th> <th style="width: 1px;">&nbsp;</th>
<th>From</th> <th>From</th>
<th>Subject</th> <th>Subject</th>
<th>Received</th> <th>Received</th>
@ -120,13 +120,22 @@ else
<Row> <Row>
<td><ActionLink Action="View" Parameters="@($"id=" + context.NotificationId.ToString())" Security="SecurityAccessLevel.View" EditMode="false" /></td> <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><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.Subject</td>
<td>@context.CreatedOn</td> <td>@context.CreatedOn</td>
</Row> </Row>
<Detail> <Detail>
<td colspan="2"></td> <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> </Detail>
</Pager> </Pager>
} }
@ -143,13 +152,22 @@ else
<Row> <Row>
<td><ActionLink Action="View" Parameters="@($"id=" + context.NotificationId.ToString())" Security="SecurityAccessLevel.View" EditMode="false" /></td> <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><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.Subject</td>
<td>@context.CreatedOn</td> <td>@context.CreatedOn</td>
</Row> </Row>
<Detail> <Detail>
<td colspan="2"></td> <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> </Detail>
</Pager> </Pager>
} }

View File

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

View File

@ -63,7 +63,7 @@ else
<label class="control-label">Photo: </label> <label class="control-label">Photo: </label>
</td> </td>
<td> <td>
<FileManager FileId="@photofileid.ToString()" @ref="filemanager" /> <FileManager FileId="@photofileid" @ref="filemanager" />
</td> </td>
</tr> </tr>
<tr> <tr>

View File

@ -2,40 +2,79 @@
@inherits ModuleBase @inherits ModuleBase
@inject IUserRoleService UserRoleService @inject IUserRoleService UserRoleService
@inject IUserService UserService @inject IUserService UserService
@inject ISettingService SettingService
@if (userroles == null) @if (userroles == null)
{ {
<p><em>Loading...</em></p> <p>
<em>Loading...</em>
</p>
} }
else 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"> <Pager Items="@userroles">
<Header> <Header>
<th>&nbsp;</th> <th style="width: 1px;">&nbsp;</th>
<th>&nbsp;</th> <th style="width: 1px;">&nbsp;</th>
<th>&nbsp;</th> <th style="width: 1px;">&nbsp;</th>
<th>Name</th> <th>Name</th>
</Header> </Header>
<Row> <Row>
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.UserId.ToString())" /></td> <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> <ActionLink Action="Edit" Parameters="@($"id=" + context.UserId.ToString())"/>
<td><ActionLink Action="Roles" Parameters="@($"id=" + context.UserId.ToString())" /></td> </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> <td>@context.User.DisplayName</td>
</Row> </Row>
</Pager> </Pager>
} }
@code { @code {
private List<UserRole> allroles;
private List<UserRole> userroles; private List<UserRole> userroles;
private string _search;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
userroles = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId); allroles = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId);
userroles = userroles.Where(item => item.Role.Name == Constants.RegisteredRole).ToList(); 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) private async Task DeleteUser(UserRole UserRole)
@ -56,4 +95,20 @@ else
AddModuleMessage(ex.Message, MessageType.Error); 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

@ -109,8 +109,8 @@
Type moduleType = Type.GetType(typename); Type moduleType = Type.GetType(typename);
if (moduleType != null) if (moduleType != null)
{ {
var moduleobject = Activator.CreateInstance(moduleType); var moduleobject = Activator.CreateInstance(moduleType) as IModuleControl;
security = (SecurityAccessLevel)moduleType.GetProperty("SecurityAccessLevel").GetValue(moduleobject, null); security = moduleobject.SecurityAccessLevel;
} }
else else
{ {

View File

@ -52,6 +52,9 @@
[Parameter] [Parameter]
public string IconName { get; set; } // optional - specifies an icon for the link - default is no icon 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() protected override void OnParametersSet()
{ {
_text = Action; _text = Action;
@ -60,6 +63,11 @@
_text = Text; _text = Text;
} }
if (IconOnly && !string.IsNullOrEmpty(IconName))
{
_text = string.Empty;
}
if (!string.IsNullOrEmpty(Parameters)) if (!string.IsNullOrEmpty(Parameters))
{ {
_parameters = Parameters; _parameters = Parameters;
@ -82,7 +90,8 @@
if (!string.IsNullOrEmpty(IconName)) 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); _url = EditUrl(Action, _parameters);
@ -101,8 +110,8 @@
var moduleType = Type.GetType(typename); var moduleType = Type.GetType(typename);
if (moduleType != null) if (moduleType != null)
{ {
var moduleobject = Activator.CreateInstance(moduleType); var moduleobject = Activator.CreateInstance(moduleType) as IModuleControl;
security = (SecurityAccessLevel)moduleType.GetProperty("SecurityAccessLevel").GetValue(moduleobject, null); security = moduleobject.SecurityAccessLevel;
} }
else else
{ {

View File

@ -1,15 +1,17 @@
@namespace Oqtane.Modules.Controls @namespace Oqtane.Modules.Controls
@inherits ModuleBase @inherits ModuleBase
@attribute [OqtaneIgnore] @attribute [OqtaneIgnore]
@inject IFolderService FolderService @inject IFolderService FolderService
@inject IFileService FileService @inject IFileService FileService
@inject IJSRuntime JsRuntime
@if (_folders != null) @if (_folders != null)
{ {
<div id="@Id" class="container-fluid px-0"> <div id="@Id" class="container-fluid px-0">
<div class="row"> <div class="row">
<div class="col"> <div class="col">
@if (ShowFolders || FolderId <= 0)
{
<div> <div>
<select class="form-control" @onchange="(e => FolderChanged(e))"> <select class="form-control" @onchange="(e => FolderChanged(e))">
@if (string.IsNullOrEmpty(Folder)) @if (string.IsNullOrEmpty(Folder))
@ -18,7 +20,7 @@
} }
@foreach (Folder folder in _folders) @foreach (Folder folder in _folders)
{ {
if (folder.FolderId == _folderid) if (folder.FolderId == FolderId)
{ {
<option value="@(folder.FolderId)" selected>@(new string('-', folder.Level * 2))@(folder.Name)</option> <option value="@(folder.FolderId)" selected>@(new string('-', folder.Level * 2))@(folder.Name)</option>
} }
@ -29,14 +31,15 @@
} }
</select> </select>
</div> </div>
@if (_showfiles) }
@if (ShowFiles)
{ {
<div> <div>
<select class="form-control" @onchange="(e => FileChanged(e))"> <select class="form-control" @onchange="(e => FileChanged(e))">
<option value="-1">&lt;Select File&gt;</option> <option value="-1">&lt;Select File&gt;</option>
@foreach (File file in _files) @foreach (File file in _files)
{ {
if (file.FileId == _fileid) if (file.FileId == FileId)
{ {
<option value="@(file.FileId)" selected>@(file.Name)</option> <option value="@(file.FileId)" selected>@(file.Name)</option>
} }
@ -48,16 +51,16 @@
</select> </select>
</div> </div>
} }
@if (_haseditpermission) @if (ShowUpload && _haseditpermission)
{ {
<div> <div>
@if (_uploadmultiple) @if (UploadMultiple)
{ {
<input type="file" id="@_fileinputid" name="file" accept="@_filter" multiple /> <input type="file" id="@_fileinputid" name="file" accept="@_filter" multiple/>
} }
else 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 id="@_progressinfoid"></span><progress id="@_progressbarid" style="width: 150px; visibility: hidden;"></progress>
<span class="float-right"> <span class="float-right">
@ -68,13 +71,13 @@
} }
</span> </span>
</div> </div>
@((MarkupString)_message)
} }
@((MarkupString) _message)
</div> </div>
@if (_image != string.Empty) @if (_image != string.Empty)
{ {
<div class="col-auto"> <div class="col-auto">
@((MarkupString)_image) @((MarkupString) _image)
</div> </div>
} }
</div> </div>
@ -84,15 +87,12 @@
@code { @code {
private string _id; private string _id;
private List<Folder> _folders; private List<Folder> _folders;
private int _folderid = -1;
private List<File> _files = new List<File>(); private List<File> _files = new List<File>();
private int _fileid = -1;
private bool _showfiles = true; private bool _showfiles = true;
private string _fileinputid = string.Empty; private string _fileinputid = string.Empty;
private string _progressinfoid = string.Empty; private string _progressinfoid = string.Empty;
private string _progressbarid = string.Empty; private string _progressbarid = string.Empty;
private string _filter = "*"; private string _filter = "*";
private bool _uploadmultiple = false;
private bool _haseditpermission = false; private bool _haseditpermission = false;
private string _message = string.Empty; private string _message = string.Empty;
private string _image = string.Empty; private string _image = string.Empty;
@ -105,19 +105,25 @@
public string Folder { get; set; } // optional - for setting a specific folder by default public string Folder { get; set; } // optional - for setting a specific folder by default
[Parameter] [Parameter]
public string FolderId { get; set; } // optional - for setting a specific folderid by default public int FolderId { get; set; } = -1; // optional - for setting a specific folderid by default
[Parameter] [Parameter]
public string ShowFiles { get; set; } // optional - for indicating whether a list of files should be displayed - default is true public bool ShowFiles { get; set; } = true; // optional - for indicating whether a list of files should be displayed - default is true
[Parameter] [Parameter]
public string FileId { get; set; } // optional - for setting a specific file by default public bool ShowUpload { get; set; } = true; // optional - for indicating whether a Upload controls should be displayed - default is true
[Parameter]
public bool ShowFolders { get; set; } = true; // optional - for indicating whether a list of folders should be displayed - default is true
[Parameter]
public int FileId { get; set; } = -1; // optional - for setting a specific file by default
[Parameter] [Parameter]
public string Filter { get; set; } // optional - comma delimited list of file types that can be selected or uploaded ie. "jpg,gif" public string Filter { get; set; } // optional - comma delimited list of file types that can be selected or uploaded ie. "jpg,gif"
[Parameter] [Parameter]
public string UploadMultiple { get; set; } // optional - enable multiple file uploads - default false public bool UploadMultiple { get; set; } = false; // optional - enable multiple file uploads - default false
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
@ -129,42 +135,30 @@
if (!string.IsNullOrEmpty(Folder)) 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; FolderId = -1;
} }
else else
{ {
_folders = await FolderService.GetFoldersAsync(ModuleState.SiteId); _folders = await FolderService.GetFoldersAsync(ModuleState.SiteId);
if (!string.IsNullOrEmpty(FolderId))
{
_folderid = int.Parse(FolderId);
}
} }
if (!string.IsNullOrEmpty(FileId)) if (FileId != -1)
{ {
_fileid = int.Parse(FileId); File file = await FileService.GetFileAsync(FileId);
if (_fileid != -1)
{
File file = await FileService.GetFileAsync(int.Parse(FileId));
if (file != null) if (file != null)
{ {
_folderid = file.FolderId; FolderId = file.FolderId;
} }
else else
{ {
_fileid = -1; // file does not exist FileId = -1; // file does not exist
} }
} }
await SetImage(); await SetImage();
}
if (!string.IsNullOrEmpty(ShowFiles))
{
_showfiles = bool.Parse(ShowFiles);
}
if (!string.IsNullOrEmpty(Filter)) if (!string.IsNullOrEmpty(Filter))
{ {
_filter = "." + Filter.Replace(",",",."); _filter = "." + Filter.Replace(",", ",.");
} }
await GetFiles(); await GetFiles();
@ -174,11 +168,6 @@
_fileinputid = _guid + "FileInput"; _fileinputid = _guid + "FileInput";
_progressinfoid = _guid + "ProgressInfo"; _progressinfoid = _guid + "ProgressInfo";
_progressbarid = _guid + "ProgressBar"; _progressbarid = _guid + "ProgressBar";
if (!string.IsNullOrEmpty(UploadMultiple))
{
_uploadmultiple = bool.Parse(UploadMultiple);
}
} }
private async Task GetFiles() private async Task GetFiles()
@ -191,11 +180,11 @@
} }
else else
{ {
Folder folder = _folders.FirstOrDefault(item => item.FolderId == _folderid); Folder folder = _folders.FirstOrDefault(item => item.FolderId == FolderId);
if (folder != null) if (folder != null)
{ {
_haseditpermission = UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, folder.Permissions); _haseditpermission = UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, folder.Permissions);
_files = await FileService.GetFilesAsync(_folderid); _files = await FileService.GetFilesAsync(FolderId);
} }
else else
{ {
@ -222,9 +211,9 @@
_message = string.Empty; _message = string.Empty;
try try
{ {
_folderid = int.Parse((string)e.Value); FolderId = int.Parse((string) e.Value);
await GetFiles(); await GetFiles();
_fileid = -1; FileId = -1;
_image = string.Empty; _image = string.Empty;
StateHasChanged(); StateHasChanged();
} }
@ -238,7 +227,7 @@
private async Task FileChanged(ChangeEventArgs e) private async Task FileChanged(ChangeEventArgs e)
{ {
_message = string.Empty; _message = string.Empty;
_fileid = int.Parse((string)e.Value); FileId = int.Parse((string) e.Value);
await SetImage(); await SetImage();
StateHasChanged(); StateHasChanged();
@ -247,19 +236,19 @@
private async Task SetImage() private async Task SetImage()
{ {
_image = string.Empty; _image = string.Empty;
if (_fileid != -1) if (FileId != -1)
{ {
File file = await FileService.GetFileAsync(_fileid); File file = await FileService.GetFileAsync(FileId);
if (file != null && file.ImageHeight != 0 && file.ImageWidth != 0) if (file != null && file.ImageHeight != 0 && file.ImageWidth != 0)
{ {
var maxwidth = 200; var maxwidth = 200;
var maxheight = 200; var maxheight = 200;
var ratioX = (double)maxwidth / (double)file.ImageWidth; var ratioX = (double) maxwidth / (double) file.ImageWidth;
var ratioY = (double)maxheight / (double)file.ImageHeight; var ratioY = (double) maxheight / (double) file.ImageHeight;
var ratio = ratioX < ratioY ? ratioX : ratioY; var ratio = ratioX < ratioY ? ratioX : ratioY;
_image = "<img src=\"" + ContentUrl(_fileid) + "\" alt=\"" + file.Name + _image = "<img src=\"" + ContentUrl(FileId) + "\" alt=\"" + file.Name +
"\" width=\"" + Convert.ToInt32(file.ImageWidth * ratio).ToString() + "\" width=\"" + Convert.ToInt32(file.ImageWidth * ratio).ToString() +
"\" height=\"" + Convert.ToInt32(file.ImageHeight * ratio).ToString() + "\" />"; "\" height=\"" + Convert.ToInt32(file.ImageHeight * ratio).ToString() + "\" />";
} }
@ -268,7 +257,7 @@
private async Task UploadFile() private async Task UploadFile()
{ {
var interop = new Interop(JsRuntime); var interop = new Interop(JSRuntime);
var upload = await interop.GetFiles(_fileinputid); var upload = await interop.GetFiles(_fileinputid);
if (upload.Length > 0) if (upload.Length > 0)
{ {
@ -281,7 +270,7 @@
} }
else else
{ {
result = await FileService.UploadFilesAsync(_folderid, upload, _guid); result = await FileService.UploadFilesAsync(FolderId, upload, _guid);
} }
if (result == string.Empty) if (result == string.Empty)
@ -295,7 +284,7 @@
var file = _files.Where(item => item.Name == upload[0]).FirstOrDefault(); var file = _files.Where(item => item.Name == upload[0]).FirstOrDefault();
if (file != null) if (file != null)
{ {
_fileid = file.FileId; FileId = file.FileId;
await SetImage(); await SetImage();
} }
} }
@ -325,21 +314,21 @@
try try
{ {
await FileService.DeleteFileAsync(_fileid); await FileService.DeleteFileAsync(FileId);
await logger.LogInformation("File Deleted {File}", _fileid); await logger.LogInformation("File Deleted {File}", FileId);
_message = "<br /><div class=\"alert alert-success\" role=\"alert\">File Deleted</div>"; _message = "<br /><div class=\"alert alert-success\" role=\"alert\">File Deleted</div>";
await GetFiles(); await GetFiles();
_fileid = -1; FileId = -1;
await SetImage(); await SetImage();
StateHasChanged(); StateHasChanged();
} }
catch (Exception ex) catch (Exception ex)
{ {
await logger.LogError(ex, "Error Deleting File {File} {Error}", _fileid, ex.Message); await logger.LogError(ex, "Error Deleting File {File} {Error}", FileId, ex.Message);
_message = "<br /><div class=\"alert alert-danger\" role=\"alert\">Error Deleting File</div>"; _message = "<br /><div class=\"alert alert-danger\" role=\"alert\">Error Deleting File</div>";
} }
} }
public int GetFileId() => _fileid; public int GetFileId() => FileId;
} }

View File

@ -10,10 +10,10 @@
<table class="table" style="width: 50%; min-width: 250px;"> <table class="table" style="width: 50%; min-width: 250px;">
<tbody> <tbody>
<tr> <tr>
<th>Role</th> <th scope="col">Role</th>
@foreach (PermissionString permission in _permissions) @foreach (PermissionString permission in _permissions)
{ {
<th style="text-align: center;">@permission.PermissionName</th> <th style="text-align: center; width: 1px;">@permission.PermissionName</th>
} }
</tr> </tr>
@foreach (Role role in _roles) @foreach (Role role in _roles)
@ -36,10 +36,10 @@
<table class="table" style="width: 50%; min-width: 250px;"> <table class="table" style="width: 50%; min-width: 250px;">
<thead> <thead>
<tr> <tr>
<th>User</th> <th scope="col">User</th>
@foreach (PermissionString permission in _permissions) @foreach (PermissionString permission in _permissions)
{ {
<th style="text-align: center;">@permission.PermissionName</th> <th style="text-align: center; width: 1px;">@permission.PermissionName</th>
} }
</tr> </tr>
</thead> </thead>
@ -48,11 +48,11 @@
{ {
string userid = "[" + user.UserId.ToString() + "]"; string userid = "[" + user.UserId.ToString() + "]";
<tr> <tr>
<td>@user.DisplayName</td> <td >@user.DisplayName</td>
@foreach (PermissionString permission in _permissions) @foreach (PermissionString permission in _permissions)
{ {
var p = permission; 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))" /> <TriStateCheckBox Value=@GetPermissionValue(p.Permissions, userid) Disabled=false OnChange="@(e => PermissionChanged(e, p.PermissionName, userid))" />
</td> </td>
} }
@ -64,9 +64,10 @@
<table class="table" style="width: 50%; min-width: 250px;"> <table class="table" style="width: 50%; min-width: 250px;">
<tbody> <tbody>
<tr> <tr>
<td style="text-align: right;"><label for="Username" class="control-label">User: </label></td> <td class="input-group">
<td><input type="text" name="Username" class="form-control" placeholder="Enter Username" @bind="@_username" /></td> <input type="text" name="Username" class="form-control" placeholder="Enter Username" @bind="@_username" />
<td style="text-align: left;"><button type="button" class="btn btn-primary" @onclick="AddUser">Add</button></td> <button type="button" class="btn btn-primary" @onclick="AddUser">Add</button>
</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

View File

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

View File

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

View File

@ -4,12 +4,12 @@
<div class="d-flex"> <div class="d-flex">
<div> <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> <h5>@_heading</h5>
</a> </a>
</div> </div>
<div class="ml-auto"> <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; <i class="oi oi-chevron-bottom"></i>&nbsp;
</a> </a>
</div> </div>

View File

@ -11,13 +11,13 @@
<li class="nav-item"> <li class="nav-item">
@if (tabPanel.Name == ActiveTab) @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) @DisplayHeading(tabPanel.Name, tabPanel.Heading)
</a> </a>
} }
else 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) @DisplayHeading(tabPanel.Name, tabPanel.Heading)
</a> </a>
} }

View File

@ -3,9 +3,8 @@
@using Oqtane.Modules.Controls @using Oqtane.Modules.Controls
@namespace Oqtane.Modules.HtmlText @namespace Oqtane.Modules.HtmlText
@inherits ModuleBase @inherits ModuleBase
@inject IHtmlTextService HtmlTextService
@inject NavigationManager NavigationManager @inject NavigationManager NavigationManager
@inject HttpClient http
@inject SiteState sitestate
@if (_content != null) @if (_content != null)
{ {
@ -14,12 +13,24 @@
<NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink> <NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink>
@if (!string.IsNullOrEmpty(_content)) @if (!string.IsNullOrEmpty(_content))
{ {
<br /><br /> <br />
<br />
<AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon"></AuditInfo> <AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon"></AuditInfo>
} }
} }
@code { @code {
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
public override string Title => "Edit Html/Text";
public override List<Resource> Resources => new List<Resource>()
{
new Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css" },
new Resource { ResourceType = ResourceType.Stylesheet, Url = "css/quill/quill1.3.6.bubble.css" },
new Resource { ResourceType = ResourceType.Stylesheet, Url = "css/quill/quill1.3.6.snow.css" }
};
private RichTextEditor RichTextEditorHtml; private RichTextEditor RichTextEditorHtml;
private string _content = null; private string _content = null;
private string _createdby; private string _createdby;
@ -27,16 +38,11 @@
private string _modifiedby; private string _modifiedby;
private DateTime _modifiedon; private DateTime _modifiedon;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
public override string Title => "Edit Html/Text";
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
try try
{ {
var htmltextservice = new HtmlTextService(http, sitestate); var htmltext = await HtmlTextService.GetHtmlTextAsync(ModuleState.ModuleId);
var htmltext = await htmltextservice.GetHtmlTextAsync(ModuleState.ModuleId);
if (htmltext != null) if (htmltext != null)
{ {
_content = htmltext.Content; _content = htmltext.Content;
@ -65,19 +71,18 @@
try try
{ {
var htmltextservice = new HtmlTextService(http, sitestate); var htmltext = await HtmlTextService.GetHtmlTextAsync(ModuleState.ModuleId);
var htmltext = await htmltextservice.GetHtmlTextAsync(ModuleState.ModuleId);
if (htmltext != null) if (htmltext != null)
{ {
htmltext.Content = content; htmltext.Content = content;
await htmltextservice.UpdateHtmlTextAsync(htmltext); await HtmlTextService.UpdateHtmlTextAsync(htmltext);
} }
else else
{ {
htmltext = new HtmlTextInfo(); htmltext = new HtmlTextInfo();
htmltext.ModuleId = ModuleState.ModuleId; htmltext.ModuleId = ModuleState.ModuleId;
htmltext.Content = content; htmltext.Content = content;
await htmltextservice.AddHtmlTextAsync(htmltext); await HtmlTextService.AddHtmlTextAsync(htmltext);
} }
await logger.LogInformation("Html/Text Content Saved {HtmlText}", htmltext); await logger.LogInformation("Html/Text Content Saved {HtmlText}", htmltext);

View File

@ -1,32 +1,28 @@
@using Oqtane.Modules.HtmlText.Services @using Oqtane.Modules.HtmlText.Services
@using Oqtane.Modules.HtmlText.Models
@namespace Oqtane.Modules.HtmlText @namespace Oqtane.Modules.HtmlText
@inherits ModuleBase @inherits ModuleBase
@inject NavigationManager NavigationManager @inject IHtmlTextService HtmlTextService
@inject HttpClient http
@inject SiteState sitestate
@((MarkupString)content) @((MarkupString)content)
@if (PageState.EditMode) @if (PageState.EditMode)
{ {
<br /> <br /><ActionLink Action="Edit" /><br /><br />
}
<ActionLink Action="Edit" />
@if (PageState.EditMode)
{
<br /><br />
} }
@code { @code {
public override List<Resource> Resources => new List<Resource>()
{
new Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css" }
};
private string content = ""; private string content = "";
protected override async Task OnParametersSetAsync() protected override async Task OnParametersSetAsync()
{ {
try try
{ {
var htmltextservice = new HtmlTextService(http, sitestate); var htmltext = await HtmlTextService.GetHtmlTextAsync(ModuleState.ModuleId);
var htmltext = await htmltextservice.GetHtmlTextAsync(ModuleState.ModuleId);
if (htmltext != null) if (htmltext != null)
{ {
content = htmltext.Content; content = htmltext.Content;

View File

@ -8,7 +8,7 @@ using Oqtane.Shared;
namespace Oqtane.Modules.HtmlText.Services namespace Oqtane.Modules.HtmlText.Services
{ {
public class HtmlTextService : ServiceBase, IHtmlTextService public class HtmlTextService : ServiceBase, IHtmlTextService, IService
{ {
private readonly SiteState _siteState; private readonly SiteState _siteState;

View File

@ -6,10 +6,13 @@ using Oqtane.Services;
using System; using System;
using Oqtane.Enums; using Oqtane.Enums;
using Oqtane.UI; using Oqtane.UI;
using System.Collections.Generic;
using Microsoft.JSInterop;
using System.Linq;
namespace Oqtane.Modules namespace Oqtane.Modules
{ {
public class ModuleBase : ComponentBase, IModuleControl public abstract class ModuleBase : ComponentBase, IModuleControl
{ {
private Logger _logger; private Logger _logger;
@ -18,6 +21,9 @@ namespace Oqtane.Modules
[Inject] [Inject]
protected ILogService LoggingService { get; set; } protected ILogService LoggingService { get; set; }
[Inject]
protected IJSRuntime JSRuntime { get; set; }
[CascadingParameter] [CascadingParameter]
protected PageState PageState { get; set; } protected PageState PageState { get; set; }
@ -27,7 +33,6 @@ namespace Oqtane.Modules
[CascadingParameter] [CascadingParameter]
protected ModuleInstance ModuleInstance { get; set; } protected ModuleInstance ModuleInstance { get; set; }
// optional interface properties // optional interface properties
public virtual SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.View; } set { } } // default security public virtual SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.View; } set { } } // default security
@ -37,6 +42,27 @@ namespace Oqtane.Modules
public virtual bool UseAdminContainer { get { return true; } } public virtual bool UseAdminContainer { get { return true; } }
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 // path method
public string ModulePath() public string ModulePath()

View File

@ -6,7 +6,7 @@
<LangVersion>7.3</LangVersion> <LangVersion>7.3</LangVersion>
<RazorLangVersion>3.0</RazorLangVersion> <RazorLangVersion>3.0</RazorLangVersion>
<Configurations>Debug;Release</Configurations> <Configurations>Debug;Release</Configurations>
<Version>0.9.1</Version> <Version>1.0.1</Version>
<Product>Oqtane</Product> <Product>Oqtane</Product>
<Authors>Shaun Walker</Authors> <Authors>Shaun Walker</Authors>
<Company>.NET Foundation</Company> <Company>.NET Foundation</Company>
@ -15,8 +15,9 @@
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl> <PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
<RepositoryUrl>https://github.com/oqtane</RepositoryUrl> <RepositoryUrl>https://github.com/oqtane</RepositoryUrl>
<RepositoryType>Git</RepositoryType> <RepositoryType>Git</RepositoryType>
<PackageReleaseNotes>Not for production use.</PackageReleaseNotes> <PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v1.0.1</PackageReleaseNotes>
<RootNamespace>Oqtane</RootNamespace> <RootNamespace>Oqtane</RootNamespace>
<IsPackable>true</IsPackable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@ -27,14 +28,14 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="3.2.0-rc1.20223.4" /> <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="3.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Build" Version="3.2.0-rc1.20223.4" PrivateAssets="all" /> <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Build" Version="3.2.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="3.1.2" /> <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="3.2.0" PrivateAssets="all" />
<PackageReference Include="System.Net.Http.Json" Version="3.2.0-rc1.20217.1" /> <PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="3.1.4" />
<PackageReference Include="System.Net.Http.Json" Version="3.2.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Oqtane.Shared\Oqtane.Shared.csproj" /> <ProjectReference Include="..\Oqtane.Shared\Oqtane.Shared.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -4,12 +4,16 @@ using System.Threading.Tasks;
using Oqtane.Services; using Oqtane.Services;
using System.Reflection; using System.Reflection;
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Net.Http.Json;
using Oqtane.Modules; using Oqtane.Modules;
using Oqtane.Shared; using Oqtane.Shared;
using Oqtane.Providers; using Oqtane.Providers;
using Microsoft.AspNetCore.Components.Authorization; using Microsoft.AspNetCore.Components.Authorization;
using System.IO.Compression;
using System.IO;
namespace Oqtane.Client namespace Oqtane.Client
{ {
@ -19,10 +23,9 @@ namespace Oqtane.Client
{ {
var builder = WebAssemblyHostBuilder.CreateDefault(args); var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("app"); builder.RootComponents.Add<App>("app");
HttpClient httpClient = new HttpClient {BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)};
builder.Services.AddSingleton( builder.Services.AddSingleton(httpClient);
new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }
);
builder.Services.AddOptions(); builder.Services.AddOptions();
// register auth services // register auth services
@ -57,14 +60,16 @@ namespace Oqtane.Client
builder.Services.AddScoped<ISqlService, SqlService>(); builder.Services.AddScoped<ISqlService, SqlService>();
builder.Services.AddScoped<ISystemService, SystemService>(); builder.Services.AddScoped<ISystemService, SystemService>();
await LoadClientAssemblies(httpClient);
// dynamically register module contexts and repository services // dynamically register module contexts and repository services
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (Assembly assembly in assemblies) foreach (Assembly assembly in assemblies)
{ {
Type[] implementationtypes = assembly.GetTypes() var implementationTypes = assembly.GetTypes()
.Where(item => item.GetInterfaces().Contains(typeof(IService))) .Where(item => item.GetInterfaces().Contains(typeof(IService)));
.ToArray();
foreach (Type implementationtype in implementationtypes) foreach (Type implementationtype in implementationTypes)
{ {
Type servicetype = Type.GetType(implementationtype.AssemblyQualifiedName.Replace(implementationtype.Name, "I" + implementationtype.Name)); Type servicetype = Type.GetType(implementationtype.AssemblyQualifiedName.Replace(implementationtype.Name, "I" + implementationtype.Name));
if (servicetype != null) if (servicetype != null)
@ -76,9 +81,62 @@ namespace Oqtane.Client
builder.Services.AddScoped(implementationtype, implementationtype); // no interface defined for service builder.Services.AddScoped(implementationtype, implementationtype); // no interface defined for service
} }
} }
assembly.GetInstances<IClientStartup>()
.ToList()
.ForEach(x => x.ConfigureServices(builder.Services));
} }
await builder.Build().RunAsync(); await builder.Build().RunAsync();
} }
private static async Task LoadClientAssemblies(HttpClient http)
{
// get list of loaded assemblies on the client
var assemblies = AppDomain.CurrentDomain.GetAssemblies().Select(a => a.GetName().Name).ToList();
// get assemblies from server and load into client app domain
var zip = await http.GetByteArrayAsync($"/~/api/Installation/load");
// asemblies and debug symbols are packaged in a zip file
using (ZipArchive archive = new ZipArchive(new MemoryStream(zip)))
{
Dictionary<string, byte[]> dlls = new Dictionary<string, byte[]>();
Dictionary<string, byte[]> pdbs = new Dictionary<string, byte[]>();
foreach (ZipArchiveEntry entry in archive.Entries)
{
if (!assemblies.Contains(Path.GetFileNameWithoutExtension(entry.Name)))
{
using (var memoryStream = new MemoryStream())
{
entry.Open().CopyTo(memoryStream);
byte[] file = memoryStream.ToArray();
switch (Path.GetExtension(entry.Name))
{
case ".dll":
dlls.Add(entry.Name, file);
break;
case ".pdb":
pdbs.Add(entry.Name, file);
break;
}
}
}
}
foreach (var item in dlls)
{
if (pdbs.ContainsKey(item.Key))
{
Assembly.Load(item.Value, pdbs[item.Key]);
}
else
{
Assembly.Load(item.Value);
}
}
}
}
} }
} }

View File

@ -5,16 +5,21 @@ using System.Linq;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net; using System.Net;
using System; using System;
using Oqtane.Shared;
namespace Oqtane.Services namespace Oqtane.Services
{ {
public class AliasService : ServiceBase, IAliasService public class AliasService : ServiceBase, IAliasService
{ {
public AliasService(HttpClient http) :base(http) { } private readonly SiteState _siteState;
private string Apiurl => CreateApiUrl("Alias"); public AliasService(HttpClient http, SiteState siteState) : base(http)
{
_siteState = siteState;
}
private string Apiurl => CreateApiUrl(_siteState.Alias, "Alias");
public async Task<List<Alias>> GetAliasesAsync() public async Task<List<Alias>> GetAliasesAsync()
{ {

View File

@ -12,7 +12,6 @@ namespace Oqtane.Services
Task UpdateModuleDefinitionAsync(ModuleDefinition moduleDefinition); Task UpdateModuleDefinitionAsync(ModuleDefinition moduleDefinition);
Task InstallModuleDefinitionsAsync(); Task InstallModuleDefinitionsAsync();
Task DeleteModuleDefinitionAsync(int moduleDefinitionId, int siteId); Task DeleteModuleDefinitionAsync(int moduleDefinitionId, int siteId);
Task LoadModuleDefinitionsAsync(int siteId, Runtime runtime);
Task CreateModuleDefinitionAsync(ModuleDefinition moduleDefinition, int moduleId); Task CreateModuleDefinitionAsync(ModuleDefinition moduleDefinition, int moduleId);
} }
} }

View File

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

View File

@ -3,14 +3,20 @@ using System.Threading.Tasks;
using System.Net.Http; using System.Net.Http;
using System.Linq; using System.Linq;
using System.Collections.Generic; using System.Collections.Generic;
using Oqtane.Shared;
namespace Oqtane.Services namespace Oqtane.Services
{ {
public class JobLogService : ServiceBase, IJobLogService public class JobLogService : ServiceBase, IJobLogService
{ {
public JobLogService(HttpClient http) :base(http) { } private readonly SiteState _siteState;
private string Apiurl => CreateApiUrl("JobLog"); public JobLogService(HttpClient http, SiteState siteState) : base(http)
{
_siteState = siteState;
}
private string Apiurl => CreateApiUrl(_siteState.Alias, "JobLog");
public async Task<List<JobLog>> GetJobLogsAsync() public async Task<List<JobLog>> GetJobLogsAsync()
{ {

View File

@ -3,14 +3,20 @@ using System.Threading.Tasks;
using System.Net.Http; using System.Net.Http;
using System.Linq; using System.Linq;
using System.Collections.Generic; using System.Collections.Generic;
using Oqtane.Shared;
namespace Oqtane.Services namespace Oqtane.Services
{ {
public class JobService : ServiceBase, IJobService public class JobService : ServiceBase, IJobService
{ {
public JobService(HttpClient http) : base(http) { } private readonly SiteState _siteState;
private string Apiurl => CreateApiUrl("Job"); public JobService(HttpClient http, SiteState siteState) : base(http)
{
_siteState = siteState;
}
private string Apiurl => CreateApiUrl(_siteState.Alias, "Job");
public async Task<List<Job>> GetJobsAsync() public async Task<List<Job>> GetJobsAsync()
{ {

View File

@ -49,46 +49,9 @@ namespace Oqtane.Services
await DeleteAsync($"{Apiurl}/{moduleDefinitionId}?siteid={siteId}"); await DeleteAsync($"{Apiurl}/{moduleDefinitionId}?siteid={siteId}");
} }
public async Task LoadModuleDefinitionsAsync(int siteId, Runtime runtime)
{
// get list of modules from the server
List<ModuleDefinition> moduledefinitions = await GetModuleDefinitionsAsync(siteId);
// download assemblies to browser when running client-side Blazor
if (runtime == Runtime.WebAssembly)
{
// get list of loaded assemblies on the client ( in the client-side hosting module the browser client has its own app domain )
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (ModuleDefinition moduledefinition in moduledefinitions)
{
// if a module has dependencies, check if they are loaded
if (moduledefinition.Dependencies != "")
{
foreach (string dependency in moduledefinition.Dependencies.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries))
{
string assemblyname = dependency.Replace(".dll", "");
if (assemblies.Where(item => item.FullName.StartsWith(assemblyname + ",")).FirstOrDefault() == null)
{
// download assembly from server and load
var bytes = await _http.GetByteArrayAsync($"{Apiurl}/load/{assemblyname}.dll");
Assembly.Load(bytes);
}
}
}
// check if the module assembly is loaded
if (assemblies.Where(item => item.FullName.StartsWith(moduledefinition.AssemblyName + ",")).FirstOrDefault() == null)
{
// download assembly from server and load
var bytes = await _http.GetByteArrayAsync($"{Apiurl}/load/{moduledefinition.AssemblyName}.dll");
Assembly.Load(bytes);
}
}
}
}
public async Task CreateModuleDefinitionAsync(ModuleDefinition moduleDefinition, int moduleId) public async Task CreateModuleDefinitionAsync(ModuleDefinition moduleDefinition, int moduleId)
{ {
await PostJsonAsync($"{Apiurl}?moduleid={moduleId.ToString()}", moduleDefinition); await PostJsonAsync($"{Apiurl}?moduleid={moduleId}", moduleDefinition);
} }
} }
} }

View File

@ -135,13 +135,13 @@ namespace Oqtane.Services
//TODO Missing content JSON validation //TODO Missing content JSON validation
} }
// create an API Url which is tenant agnostic ( for use with entities in the MasterDB ) // create an API Url which is tenant agnostic ( for use during installation )
public string CreateApiUrl(string serviceName) public string CreateApiUrl(string serviceName)
{ {
return CreateApiUrl(null, serviceName); return CreateApiUrl(null, serviceName);
} }
// create an API Url which is tenant aware ( for use with entities in the TenantDB ) // create an API Url which is tenant aware ( for use with repositories )
public string CreateApiUrl(Alias alias, string serviceName) public string CreateApiUrl(Alias alias, string serviceName)
{ {
string apiurl = "/"; string apiurl = "/";

View File

@ -1,4 +1,5 @@
using Oqtane.Models; using System;
using Oqtane.Models;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Net.Http; using System.Net.Http;
using System.Linq; using System.Linq;
@ -106,7 +107,7 @@ namespace Oqtane.Services
foreach (KeyValuePair<string, string> kvp in settings) foreach (KeyValuePair<string, string> kvp in settings)
{ {
Setting setting = settingsList.FirstOrDefault(item => item.SettingName == kvp.Key); Setting setting = settingsList.FirstOrDefault(item => item.SettingName.Equals(kvp.Key,StringComparison.OrdinalIgnoreCase));
if (setting == null) if (setting == null)
{ {
setting = new Setting(); setting = new Setting();
@ -152,7 +153,7 @@ namespace Oqtane.Services
public string GetSetting(Dictionary<string, string> settings, string settingName, string defaultValue) public string GetSetting(Dictionary<string, string> settings, string settingName, string defaultValue)
{ {
string value = defaultValue; string value = defaultValue;
if (settings.ContainsKey(settingName)) if (settings != null && settings.ContainsKey(settingName))
{ {
value = settings[settingName]; value = settings[settingName];
} }
@ -161,6 +162,10 @@ namespace Oqtane.Services
public Dictionary<string, string> SetSetting(Dictionary<string, string> settings, string settingName, string settingValue) 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)) if (settings.ContainsKey(settingName))
{ {
settings[settingName] = settingValue; settings[settingName] = settingValue;

View File

@ -1,4 +1,5 @@
using Oqtane.Models; using Oqtane.Models;
using Oqtane.Shared;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -6,9 +7,14 @@ namespace Oqtane.Services
{ {
public class SqlService : ServiceBase, ISqlService public class SqlService : ServiceBase, ISqlService
{ {
public SqlService(HttpClient http) : base(http) { } private readonly SiteState _siteState;
private string Apiurl => CreateApiUrl("Sql"); public SqlService(HttpClient http, SiteState siteState) : base(http)
{
_siteState = siteState;
}
private string Apiurl => CreateApiUrl(_siteState.Alias, "Sql");
public async Task<SqlQuery> ExecuteQueryAsync(SqlQuery sqlquery) public async Task<SqlQuery> ExecuteQueryAsync(SqlQuery sqlquery)
{ {

View File

@ -3,14 +3,20 @@ using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Oqtane.Shared;
namespace Oqtane.Services namespace Oqtane.Services
{ {
public class TenantService : ServiceBase, ITenantService public class TenantService : ServiceBase, ITenantService
{ {
public TenantService(HttpClient http) : base(http) { } private readonly SiteState _siteState;
private string Apiurl => CreateApiUrl("Tenant"); public TenantService(HttpClient http, SiteState siteState) : base(http)
{
_siteState = siteState;
}
private string Apiurl => CreateApiUrl(_siteState.Alias, "Tenant");
public async Task<List<Tenant>> GetTenantsAsync() public async Task<List<Tenant>> GetTenantsAsync()
{ {

View File

@ -1,108 +1,54 @@
using Oqtane.Models; using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Reflection; using Oqtane.Models;
using System;
using Oqtane.Shared; using Oqtane.Shared;
namespace Oqtane.Services namespace Oqtane.Services
{ {
public class ThemeService : ServiceBase, IThemeService 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() public async Task<List<Theme>> GetThemesAsync()
{ {
List<Theme> themes = await GetJsonAsync<List<Theme>>(Apiurl); List<Theme> themes = await GetJsonAsync<List<Theme>>(ApiUrl);
// get list of loaded assemblies
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (Theme theme in themes)
{
if (theme.Dependencies != "")
{
foreach (string dependency in theme.Dependencies.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries))
{
string assemblyname = dependency.Replace(".dll", "");
if (assemblies.Where(item => item.FullName.StartsWith(assemblyname + ",")).FirstOrDefault() == null)
{
// download assembly from server and load
var bytes = await _http.GetByteArrayAsync($"{Apiurl}/load/{assemblyname}.dll");
Assembly.Load(bytes);
}
}
}
if (assemblies.Where(item => item.FullName.StartsWith(theme.AssemblyName + ",")).FirstOrDefault() == null)
{
// download assembly from server and load
var bytes = await _http.GetByteArrayAsync($"{Apiurl}/load/{theme.AssemblyName}.dll");
Assembly.Load(bytes);
}
}
return themes.OrderBy(item => item.Name).ToList(); 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>(); return themes.SelectMany(item => item.Themes).ToList();
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;
} }
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>(); return themes.Where(item => Utilities.GetTypeName(themeName).StartsWith(Utilities.GetTypeName(item.ThemeName)))
foreach (Theme theme in themes) .SelectMany(item => item.Layouts).ToList();
{
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;
} }
public Dictionary<string, string> GetContainerTypes(List<Theme> themes) public List<ThemeControl> GetContainerControls(List<Theme> themes, string themeName)
{ {
var selectableContainers = new Dictionary<string, string>(); return themes.Where(item => Utilities.GetTypeName(themeName).StartsWith(Utilities.GetTypeName(item.ThemeName)))
foreach (Theme theme in themes) .SelectMany(item => item.Containers).ToList();
{
foreach (string container in theme.ContainerControls.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries))
{
selectableContainers.Add(container, theme.Name + " - " + @Utilities.GetTypeNameLastSegment(container, 0));
}
}
return selectableContainers;
} }
public async Task InstallThemesAsync() public async Task InstallThemesAsync()
{ {
await GetJsonAsync<List<string>>($"{Apiurl}/install"); await GetJsonAsync<List<string>>($"{ApiUrl}/install");
} }
public async Task DeleteThemeAsync(string themeName) public async Task DeleteThemeAsync(string themeName)
{ {
await DeleteAsync($"{Apiurl}/{themeName}"); await DeleteAsync($"{ApiUrl}/{themeName}");
} }
} }
} }

View File

@ -28,9 +28,13 @@
@code { @code {
public override string Panes => "Content"; public override string Panes => "Content";
protected override async Task OnParametersSetAsync() public override List<Resource> Resources => new List<Resource>()
{ {
await IncludeCSS("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,63 +1,12 @@
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using Oqtane.Shared;
using Oqtane.Models; using Oqtane.Models;
using Oqtane.UI;
namespace Oqtane.Themes 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] [CascadingParameter]
protected Module ModuleState { get; set; } protected Module ModuleState { get; set; }
public virtual string Name { 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 @namespace Oqtane.Themes.Controls
@inherits ThemeControlBase @inherits ThemeControlBase
@attribute [OqtaneIgnore]
@if (BreadCrumbPages.Any()) @if (BreadCrumbPages.Any())
{ {

View File

@ -1,6 +1,5 @@
@namespace Oqtane.Themes.Controls @namespace Oqtane.Themes.Controls
@inherits ThemeControlBase @inherits ThemeControlBase
@attribute [OqtaneIgnore]
@inject NavigationManager NavigationManager @inject NavigationManager NavigationManager
@inject IUserService UserService @inject IUserService UserService
@inject IModuleDefinitionService ModuleDefinitionService @inject IModuleDefinitionService ModuleDefinitionService
@ -9,8 +8,9 @@
@inject IPageService PageService @inject IPageService PageService
@inject IPageModuleService PageModuleService @inject IPageModuleService PageModuleService
@inject ILogService logger @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"> <div class="app-controlpanel" style="@_display">
@ -49,6 +49,21 @@
<button class="btn btn-danger btn-block mx-auto" @onclick="ConfirmDelete">Delete</button> <button class="btn btn-danger btn-block mx-auto" @onclick="ConfirmDelete">Delete</button>
</div> </div>
</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) @if (_deleteConfirmation)
@ -78,18 +93,18 @@
<div class="row"> <div class="row">
<div class="col text-center"> <div class="col text-center">
<label for="Module" class="control-label">Module Management: </label> <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="new">Add New Module</option>
<option value="existing">Add Existing Module</option> <option value="existing">Add Existing Module</option>
</select> </select>
@if (_moduleType == "new") @if (ModuleType == "new")
{ {
@if (_moduleDefinitions != null) @if (_moduleDefinitions != null)
{ {
<select class="form-control" @onchange="(e => CategoryChanged(e))"> <select class="form-control" @onchange="(e => CategoryChanged(e))">
@foreach (var category in _categories) @foreach (var category in _categories)
{ {
if (category == _category) if (category == Category)
{ {
<option value="@category" selected>@category Modules</option> <option value="@category" selected>@category Modules</option>
} }
@ -100,7 +115,7 @@
} }
</select> </select>
<select class="form-control" @onchange="(e => ModuleChanged(e))"> <select class="form-control" @onchange="(e => ModuleChanged(e))">
@if (_moduleDefinitionName == "-") @if (ModuleDefinitionName == "-")
{ {
<option value="-" selected>&lt;Select Module&gt;</option> <option value="-" selected>&lt;Select Module&gt;</option>
} }
@ -116,7 +131,7 @@
} }
} }
</select> </select>
@((MarkupString)@_description) @((MarkupString) Description)
} }
} }
else else
@ -128,7 +143,7 @@
<option value="@p.PageId">@p.Name</option> <option value="@p.PageId">@p.Name</option>
} }
</select> </select>
<select class="form-control" @bind="@_moduleId"> <select class="form-control" @bind="@ModuleId">
<option value="-">&lt;Select Module&gt;</option> <option value="-">&lt;Select Module&gt;</option>
@foreach (Module module in _modules) @foreach (Module module in _modules)
{ {
@ -141,28 +156,30 @@
<div class="row"> <div class="row">
<div class="col text-center"> <div class="col text-center">
<label for="Title" class="control-label">Title: </label> <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> </div>
@if (_pane.Length > 1)
{
<div class="row"> <div class="row">
<div class="col text-center"> <div class="col text-center">
<label for="Pane" class="control-label">Pane: </label> <label for="Pane" class="control-label">Pane: </label>
<select class="form-control" @bind="@_pane"> <select class="form-control" @bind="@Pane">
<option value="">&lt;Select Pane&gt;</option> @foreach (string pane in PageState.Page.Panes)
@foreach (string pane in PageState.Page.Panes.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries))
{ {
<option value="@pane">@pane Pane</option> <option value="@pane">@pane Pane</option>
} }
</select> </select>
</div> </div>
</div> </div>
}
<div class="row"> <div class="row">
<div class="col text-center"> <div class="col text-center">
<label for="Container" class="control-label">Container: </label> <label for="Container" class="control-label">Container: </label>
<select class="form-control" @bind="@_containerType"> <select class="form-control" @bind="@ContainerType">
@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> </select>
</div> </div>
@ -171,13 +188,13 @@
<br /> <br />
<button type="button" class="btn btn-primary btn-block mx-auto" @onclick="@AddModule">Add Module To Page</button> <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> </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.Page.EditMode)
{ {
@ -202,192 +219,179 @@
} }
} }
@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"> <button type="button" class="btn @ButtonClass" @onclick="ShowControlPanel">
<span class="oi oi-cog"></span> <span class="oi oi-cog"></span>
</button> </button>
} }
@code { @code{
private bool _deleteConfirmation = false; private bool _deleteConfirmation = false;
private string _moduleType = "new";
private List<string> _categories = new List<string>(); private List<string> _categories = new List<string>();
private List<ModuleDefinition> _allModuleDefinitions; private List<ModuleDefinition> _allModuleDefinitions;
private List<ModuleDefinition> _moduleDefinitions; private List<ModuleDefinition> _moduleDefinitions;
private List<Page> _pages = new List<Page>(); private List<Page> _pages = new List<Page>();
private string _pageId = "-";
private string _moduleId = "-";
private List<Module> _modules = new List<Module>(); private List<Module> _modules = new List<Module>();
private Dictionary<string, string> _containers = new Dictionary<string, string>(); private List<ThemeControl> _containers = new List<ThemeControl>();
private string _moduleDefinitionName = "-";
private string _category = "Common";
private string _description = "";
private string _pane = "";
private string _title = "";
private string _containerType = "";
private string _display = "display: none;"; private string _display = "display: none;";
private string _message = ""; private string _category = "Common";
[Parameter] protected string PageId { get; private set; } = "-";
public string ButtonClass { get; set; } protected string ModuleId { get; private set; } = "-";
protected string ModuleType { get; private set; } = "new";
protected string ModuleDefinitionName { get; private set; } = "-";
[Parameter] protected string Category
public string CardClass { get; set; }
[Parameter]
public string HeaderClass { get; set; }
[Parameter]
public string BodyClass { get; set; }
protected override async Task OnParametersSetAsync()
{ {
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
{ {
CardClass = "card border-secondary mb-3"; get => _pane;
private set
{
if (_pane != value)
{
_pane = value;
_ = UpdateSettingsAsync();
}
}
} }
if (string.IsNullOrEmpty(HeaderClass))
{
HeaderClass = "card-header";
}
if (string.IsNullOrEmpty(BodyClass)) protected string Description { get; private set; } = "";
{
BodyClass = "card-body";
}
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(); _pages?.Clear();
foreach (Page p in PageState.Pages) 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); _pages.Add(p);
} }
} }
await LoadSettingsAsync();
var panes = PageState.Page.Panes.Split(new[] {';'}, StringSplitOptions.RemoveEmptyEntries);
_pane = panes.Count() == 1 ? panes.SingleOrDefault() : "";
var themes = await ThemeService.GetThemesAsync(); var themes = await ThemeService.GetThemesAsync();
_containers = ThemeService.GetContainerTypes(themes); _containers = ThemeService.GetContainerControls(themes, PageState.Page.ThemeType);
_containerType = PageState.Site.DefaultContainerType; ContainerType = PageState.Site.DefaultContainerType;
_allModuleDefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync(PageState.Site.SiteId); _allModuleDefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync(PageState.Site.SiteId);
_moduleDefinitions = _allModuleDefinitions.Where(item => item.Categories.Contains(Category)).ToList();
_categories = new List<string>(); _categories = _allModuleDefinitions.SelectMany(m => m.Categories.Split(',')).Distinct().ToList();
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 = "";
} }
} }
private void CategoryChanged(ChangeEventArgs e) private void CategoryChanged(ChangeEventArgs e)
{ {
_category = (string) e.Value; Category = (string) e.Value;
_moduleDefinitions = _allModuleDefinitions.Where(item => item.Categories.Contains(_category)).ToList();
_moduleDefinitionName = "-";
_description = "";
StateHasChanged();
} }
private void ModuleChanged(ChangeEventArgs e) private void ModuleChanged(ChangeEventArgs e)
{ {
_moduleDefinitionName = (string)e.Value; ModuleDefinitionName = (string) e.Value;
if (_moduleDefinitionName != "-") if (ModuleDefinitionName != "-")
{ {
var moduleDefinition = _moduleDefinitions.FirstOrDefault(item => item.ModuleDefinitionName == _moduleDefinitionName); var moduleDefinition = _moduleDefinitions.FirstOrDefault(item => item.ModuleDefinitionName == ModuleDefinitionName);
_description = "<br /><div class=\"alert alert-info\" role=\"alert\">" + moduleDefinition.Description + "</div>"; Description = "<br /><div class=\"alert alert-info\" role=\"alert\">" + moduleDefinition.Description + "</div>";
} }
else else
{ {
_description = ""; Description = "";
} }
StateHasChanged(); StateHasChanged();
} }
private void PageChanged(ChangeEventArgs e) private void PageChanged(ChangeEventArgs e)
{ {
_pageId = (string) e.Value; PageId = (string) e.Value;
_modules?.Clear(); if (PageId != "-")
if (_pageId != "-")
{ {
foreach (Module module in PageState.Modules.Where(item => item.PageId == int.Parse(_pageId) && !item.IsDeleted)) _modules = PageState.Modules
{ .Where(module => module.PageId == int.Parse(PageId)
if (UserSecurity.IsAuthorized(PageState.User,PermissionNames.View, module.Permissions)) && !module.IsDeleted
{ && UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, module.Permissions))
_modules.Add(module); .ToList();
} }
} ModuleId = "-";
}
_moduleId = "-";
StateHasChanged(); StateHasChanged();
} }
private async Task AddModule() 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 module = new Module();
module.SiteId = PageState.Site.SiteId; module.SiteId = PageState.Site.SiteId;
module.PageId = PageState.Page.PageId; module.PageId = PageState.Page.PageId;
module.ModuleDefinitionName = _moduleDefinitionName; module.ModuleDefinitionName = ModuleDefinitionName;
module.AllPages = false; module.AllPages = false;
module.Permissions = PageState.Page.Permissions; module.Permissions = PageState.Page.Permissions;
module = await ModuleService.AddModuleAsync(module); module = await ModuleService.AddModuleAsync(module);
_moduleId = module.ModuleId.ToString(); ModuleId = module.ModuleId.ToString();
} }
var pageModule = new PageModule var pageModule = new PageModule
{ {
PageId = PageState.Page.PageId, PageId = PageState.Page.PageId,
ModuleId = int.Parse(_moduleId), ModuleId = int.Parse(ModuleId),
Title = _title Title = Title
}; };
if (pageModule.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 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.Order = int.MaxValue;
pageModule.ContainerType = _containerType; pageModule.ContainerType = ContainerType;
if (pageModule.ContainerType == PageState.Site.DefaultContainerType) if (pageModule.ContainerType == PageState.Site.DefaultContainerType)
{ {
@ -397,32 +401,23 @@
await PageModuleService.AddPageModuleAsync(pageModule); await PageModuleService.AddPageModuleAsync(pageModule);
await PageModuleService.UpdatePageModuleOrderAsync(pageModule.PageId, pageModule.Pane); await PageModuleService.UpdatePageModuleOrderAsync(pageModule.PageId, pageModule.Pane);
_message = "<br /><div class=\"alert alert-success\" role=\"alert\">Module Added To Page</div>"; Message = "<br /><div class=\"alert alert-success\" role=\"alert\">Module Added To Page</div>";
_moduleDefinitionName = "-";
_description = "";
_pane = "";
_title = "";
_containerType = "";
_pageId = "-";
_moduleId = "-";
NavigationManager.NavigateTo(NavigateUrl()); NavigationManager.NavigateTo(NavigateUrl());
} }
else 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 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) 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) if (EditMode)
{ {
@ -432,6 +427,7 @@
{ {
PageState.EditMode = true; PageState.EditMode = true;
} }
NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, "edit=" + ((PageState.EditMode) ? "1" : "0"))); NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, "edit=" + ((PageState.EditMode) ? "1" : "0")));
} }
else else
@ -447,14 +443,14 @@
private void ShowControlPanel() private void ShowControlPanel()
{ {
_message = ""; Message = "";
_display = "width: 25%; min-width: 375px;"; _display = "width: 25%; min-width: 375px;";
StateHasChanged(); StateHasChanged();
} }
private void HideControlPanel() private void HideControlPanel()
{ {
_message = ""; Message = "";
_display = "width: 0%;"; _display = "width: 0%;";
StateHasChanged(); StateHasChanged();
} }
@ -503,6 +499,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() private void ConfirmDelete()
{ {
_deleteConfirmation = !_deleteConfirmation; _deleteConfirmation = !_deleteConfirmation;
@ -536,4 +587,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 @namespace Oqtane.Themes.Controls
@inherits LoginBase @inherits LoginBase
@attribute [OqtaneIgnore]
<span class="app-login"> <span class="app-login">
<AuthorizeView> <AuthorizeView>

View File

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

View File

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

View File

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

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
using Oqtane.Models; using Oqtane.Models;
@ -16,6 +17,7 @@ namespace Oqtane.Themes.Controls
{ {
[Inject] public NavigationManager NavigationManager { get; set; } [Inject] public NavigationManager NavigationManager { get; set; }
[Inject] public IPageModuleService PageModuleService { get; set; } [Inject] public IPageModuleService PageModuleService { get; set; }
[Inject] public IModuleService ModuleService { get; set; }
protected List<ActionViewModel> Actions; protected List<ActionViewModel> Actions;
@ -30,14 +32,23 @@ namespace Oqtane.Themes.Controls
if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, ModuleState.Permissions)) 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 {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 { Name = "Unpublish Module", Action = async (s, m) => await Unpublish(s, m) });
}
else
{
actionList.Add(new ActionViewModel { Name = "Publish Module", Action = async (s, m) => await Publish(s, m) });
}
actionList.Add(new ActionViewModel { Name = "Delete Module", Action = async (u, m) => await DeleteModule(u, m) });
if (ModuleState.ModuleDefinition != null && ModuleState.ModuleDefinition.ServerManagerType != "") if (ModuleState.ModuleDefinition != null && ModuleState.ModuleDefinition.ServerManagerType != "")
{ {
actionList.Add(new ActionViewModel { Name = "" });
actionList.Add(new ActionViewModel {Name = "Import Content", Action = async (u, m) => await EditUrlAsync(u, m.ModuleId, "Import")}); 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 = "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 = ""}); actionList.Add(new ActionViewModel {Name = ""});
if (ModuleState.PaneModuleIndex > 0) if (ModuleState.PaneModuleIndex > 0)
@ -60,7 +71,7 @@ namespace Oqtane.Themes.Controls
actionList.Add(new ActionViewModel {Name = "Move To Bottom", Action = async (s, m) => await MoveBottom(s, m)}); actionList.Add(new ActionViewModel {Name = "Move To Bottom", Action = async (s, m) => await MoveBottom(s, m)});
} }
foreach (string pane in PageState.Page.Panes.Split(new[] {';'}, StringSplitOptions.RemoveEmptyEntries)) foreach (string pane in PageState.Page.Panes)
{ {
if (pane != ModuleState.Pane) if (pane != ModuleState.Pane)
{ {
@ -121,6 +132,42 @@ namespace Oqtane.Themes.Controls
return url; 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) private async Task<string> MoveTop(string s, PageModule pagemodule)
{ {
pagemodule.Order = 0; pagemodule.Order = 0;

View File

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

View File

@ -2,6 +2,6 @@
{ {
public interface IContainerControl public interface IContainerControl
{ {
string Name { get; }
} }
} }

View File

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

View File

@ -1,7 +0,0 @@
namespace Oqtane.Themes
{
public interface IThemeControl
{
string Panes { get; } // identifies all panes in a theme ( delimited by ";" ) - assumed to be a layout if no panes specified
}
}

View File

@ -1,19 +1,7 @@
using Microsoft.AspNetCore.Components; namespace Oqtane.Themes
using Oqtane.Shared;
using Oqtane.UI;
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> </div>
</div> </div>
@code {
public override string Name => "Standard Header";
}

View File

@ -17,13 +17,16 @@
</main> </main>
@code { @code {
public override string Name => "Default";
public override string Panes => string.Empty; public override string Panes => string.Empty;
protected override async Task OnParametersSetAsync() public override List<Resource> Resources => new List<Resource>()
{ {
// go to https://www.bootstrapcdn.com/bootswatch/ and take your favorite theme 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" },
//<link href="https://stackpath.bootstrapcdn.com/bootswatch/4.4.1/cyborg/bootstrap.min.css" rel="stylesheet" integrity="sha384-l7xaoY0cJM4h9xh1RfazbgJVUZvdtyLWPueWNtLAphf/UbBgOVzqbOTogxPwYLHM" crossorigin="anonymous"> new Resource { ResourceType = ResourceType.Stylesheet, Url = ThemePath() + "Theme.css" },
await LoadBootstrapTheme("https://stackpath.bootstrapcdn.com/bootswatch/4.4.1/cyborg/bootstrap.min.css","sha384-l7xaoY0cJM4h9xh1RfazbgJVUZvdtyLWPueWNtLAphf/UbBgOVzqbOTogxPwYLHM"); 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" },
await IncludeCSS("Theme.css"); 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> </div>
@code { @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

@ -0,0 +1,13 @@
@namespace Oqtane.Themes.OqtaneTheme
@inherits ContainerBase
<div class="container">
@if (PageState.EditMode)
{
<ModuleActions />
}
<ModuleInstance />
</div>
@code {
public override string Name => "No Header";
}

View File

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

View File

@ -1,41 +1,55 @@
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop; using Microsoft.JSInterop;
using Oqtane.Models;
using Oqtane.Shared; using Oqtane.Shared;
using Oqtane.UI; using Oqtane.UI;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Oqtane.Themes namespace Oqtane.Themes
{ {
public class ThemeBase : ComponentBase, IThemeControl public abstract class ThemeBase : ComponentBase, IThemeControl
{ {
[Inject] [Inject]
protected IJSRuntime JSRuntime { get; set; } protected IJSRuntime JSRuntime { get; set; }
// optional interface properties
[CascadingParameter] [CascadingParameter]
protected PageState PageState { get; set; } 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 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() public string ThemePath()
{ {
return "Themes/" + GetType().Namespace + "/"; return "Themes/" + GetType().Namespace + "/";
} }
public async Task IncludeCSS(string Url) // url methods
{
if (!Url.StartsWith("http"))
{
Url = ThemePath() + Url;
}
var interop = new Interop(JSRuntime);
await interop.IncludeCSS("Theme", Url);
}
public async Task LoadBootstrapTheme(string url, string integrity = null)
{
var interop = new Interop(JSRuntime);
string crossorigin = string.IsNullOrEmpty(integrity) ? string.Empty : "anonymous";
await interop.IncludeLink("bootstrap", "stylesheet", url, "text/css", integrity, crossorigin);
}
public string NavigateUrl() public string NavigateUrl()
{ {
@ -66,5 +80,10 @@ namespace Oqtane.Themes
{ {
return Utilities.EditUrl(PageState.Alias.Path, path, moduleid, action, parameters); 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.Shared;
using Oqtane.UI;
namespace Oqtane.Themes 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

@ -27,18 +27,13 @@
DynamicComponent = builder => DynamicComponent = builder =>
{ {
Type containerType = Type.GetType(container); Type containerType = Type.GetType(container);
if (containerType != null) if (containerType == null)
{ {
// fallback
containerType = Type.GetType(Constants.DefaultContainer);
}
builder.OpenComponent(0, containerType); builder.OpenComponent(0, containerType);
builder.CloseComponent(); 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();
}
}; };
} }
} }

View File

@ -3,11 +3,13 @@
@inject IInstallationService InstallationService @inject IInstallationService InstallationService
@inject ISiteService SiteService @inject ISiteService SiteService
@inject IUserService UserService @inject IUserService UserService
@inject IJSRuntime JSRuntime
<div class="container"> <div class="container">
<div class="row"> <div class="row">
<div class="mx-auto text-center"> <div class="mx-auto text-center">
<img src="oqtane.png" /> <img src="oqtane-black.png" />
<div style="font-weight: bold">Version: @Constants.Version</div>
</div> </div>
</div> </div>
<hr class="app-rule" /> <hr class="app-rule" />
@ -137,6 +139,15 @@
private string _integratedSecurityDisplay = "display: none;"; private string _integratedSecurityDisplay = "display: none;";
private string _loadingDisplay = "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) private void SetIntegratedSecurity(ChangeEventArgs e)
{ {
_integratedSecurityDisplay = Convert.ToBoolean((string)e.Value) _integratedSecurityDisplay = Convert.ToBoolean((string)e.Value)

View File

@ -1,6 +1,5 @@
using Microsoft.AspNetCore.Components; using Microsoft.JSInterop;
using Microsoft.JSInterop; using Oqtane.Models;
using System;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Oqtane.UI namespace Oqtane.UI
@ -18,8 +17,8 @@ namespace Oqtane.UI
{ {
try try
{ {
_jsRuntime.InvokeAsync<string>( _jsRuntime.InvokeVoidAsync(
"interop.setCookie", "Oqtane.Interop.setCookie",
name, value, days); name, value, days);
return Task.CompletedTask; return Task.CompletedTask;
} }
@ -34,7 +33,7 @@ namespace Oqtane.UI
try try
{ {
return _jsRuntime.InvokeAsync<string>( return _jsRuntime.InvokeAsync<string>(
"interop.getCookie", "Oqtane.Interop.getCookie",
name); name);
} }
catch catch
@ -47,8 +46,8 @@ namespace Oqtane.UI
{ {
try try
{ {
_jsRuntime.InvokeAsync<string>( _jsRuntime.InvokeVoidAsync(
"interop.updateTitle", "Oqtane.Interop.updateTitle",
title); title);
return Task.CompletedTask; 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 try
{ {
_jsRuntime.InvokeAsync<string>( _jsRuntime.InvokeVoidAsync(
"interop.includeMeta", "Oqtane.Interop.includeMeta",
id, attribute, name, content); id, attribute, name, content, key);
return Task.CompletedTask; return Task.CompletedTask;
} }
catch 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 try
{ {
_jsRuntime.InvokeAsync<string>( _jsRuntime.InvokeVoidAsync(
"interop.includeLink", "Oqtane.Interop.includeLink",
id, rel, url, type, integrity, crossorigin); id, rel, href, type, integrity, crossorigin, key);
return Task.CompletedTask; return Task.CompletedTask;
} }
catch 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 try
{ {
_jsRuntime.InvokeAsync<string>( _jsRuntime.InvokeVoidAsync(
"interop.includeScript", "Oqtane.Interop.includeLinks",
id, src, content, location, integrity, crossorigin); (object) links);
return Task.CompletedTask; return Task.CompletedTask;
} }
catch catch
@ -103,13 +102,42 @@ 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 try
{ {
_jsRuntime.InvokeAsync<string>( _jsRuntime.InvokeVoidAsync(
"interop.includeLink", "Oqtane.Interop.includeScript",
id, "stylesheet", url, "text/css"); id, src, integrity, crossorigin, content, location, key);
return Task.CompletedTask;
}
catch
{
return Task.CompletedTask;
}
}
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.InvokeVoidAsync(
"Oqtane.Interop.removeElementsById",
prefix, first, last);
return Task.CompletedTask; return Task.CompletedTask;
} }
catch catch
@ -123,7 +151,7 @@ namespace Oqtane.UI
try try
{ {
return _jsRuntime.InvokeAsync<string>( return _jsRuntime.InvokeAsync<string>(
"interop.getElementByName", "Oqtane.Interop.getElementByName",
name); name);
} }
catch catch
@ -136,8 +164,8 @@ namespace Oqtane.UI
{ {
try try
{ {
_jsRuntime.InvokeAsync<string>( _jsRuntime.InvokeVoidAsync(
"interop.submitForm", "Oqtane.Interop.submitForm",
path, fields); path, fields);
return Task.CompletedTask; return Task.CompletedTask;
} }
@ -152,7 +180,7 @@ namespace Oqtane.UI
try try
{ {
return _jsRuntime.InvokeAsync<string[]>( return _jsRuntime.InvokeAsync<string[]>(
"interop.getFiles", "Oqtane.Interop.getFiles",
id); id);
} }
catch catch
@ -165,8 +193,8 @@ namespace Oqtane.UI
{ {
try try
{ {
_jsRuntime.InvokeAsync<string>( _jsRuntime.InvokeVoidAsync(
"interop.uploadFiles", "Oqtane.Interop.uploadFiles",
posturl, folder, id); posturl, folder, id);
return Task.CompletedTask; return Task.CompletedTask;
} }
@ -175,5 +203,36 @@ namespace Oqtane.UI
return Task.CompletedTask; 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

@ -32,7 +32,7 @@
} }
else else
{ {
_paneadminborder = ""; _paneadminborder = "container";
_panetitle = ""; _panetitle = "";
} }

View File

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

View File

@ -11,7 +11,6 @@
@inject IPageService PageService @inject IPageService PageService
@inject IUserService UserService @inject IUserService UserService
@inject IModuleService ModuleService @inject IModuleService ModuleService
@inject IModuleDefinitionService ModuleDefinitionService
@inject ILogService LogService @inject ILogService LogService
@implements IHandleAfterRender @implements IHandleAfterRender
@ -91,7 +90,7 @@
// parse querystring // parse querystring
var querystring = ParseQueryString(uri.Query); 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")) if (querystring.ContainsKey("reload"))
{ {
reload = Reload.Site; reload = Reload.Site;
@ -157,7 +156,6 @@
if (PageState == null || reload >= Reload.Site) if (PageState == null || reload >= Reload.Site)
{ {
await ModuleDefinitionService.LoadModuleDefinitionsAsync(site.SiteId, runtime);
pages = await PageService.GetPagesAsync(site.SiteId); pages = await PageService.GetPagesAsync(site.SiteId);
} }
else else
@ -175,13 +173,13 @@
if (alias.Path != "") if (alias.Path != "")
{ {
path = path.Replace(alias.Path + "/", ""); path = path.Substring(alias.Path.Length + 1);
} }
// extract admin route elements from path // extract admin route elements from path
var segments = path.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries); var segments = path.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
int result; int result;
// check if path has moduleid and control specification ie. page/moduleid/control/ // check if path has moduleid and action specification ie. pagename/moduleid/action/
if (segments.Length >= 2 && int.TryParse(segments[segments.Length - 2], out result)) if (segments.Length >= 2 && int.TryParse(segments[segments.Length - 2], out result))
{ {
action = segments[segments.Length - 1]; action = segments[segments.Length - 1];
@ -190,7 +188,7 @@
} }
else else
{ {
// check if path has only moduleid specification ie. page/moduleid/ // check if path has moduleid specification ie. pagename/moduleid/
if (segments.Length >= 1 && int.TryParse(segments[segments.Length - 1], out result)) if (segments.Length >= 1 && int.TryParse(segments[segments.Length - 1], out result))
{ {
moduleid = result; moduleid = result;
@ -240,21 +238,7 @@
{ {
page = await ProcessPage(page, site, user); page = await ProcessPage(page, site, user);
_pagestate = new PageState if (PageState != null && (PageState.ModuleId != moduleid || PageState.Action != action))
{
Alias = alias,
Site = site,
Pages = pages,
Page = page,
User = user,
Uri = new Uri(_absoluteUri, UriKind.Absolute),
QueryString = querystring,
ModuleId = moduleid,
Action = action,
Runtime = runtime
};
if (PageState != null && (PageState.ModuleId != _pagestate.ModuleId || PageState.Action != _pagestate.Action))
{ {
reload = Reload.Page; reload = Reload.Page;
} }
@ -262,15 +246,29 @@
if (PageState == null || reload >= Reload.Page) if (PageState == null || reload >= Reload.Page)
{ {
modules = await ModuleService.GetModulesAsync(site.SiteId); modules = await ModuleService.GetModulesAsync(site.SiteId);
modules = ProcessModules(modules, page.PageId, _pagestate.ModuleId, _pagestate.Action, page.Panes, site.DefaultContainerType); (page, modules) = ProcessModules(page, modules, moduleid, action, (!string.IsNullOrEmpty(page.DefaultContainerType)) ? page.DefaultContainerType : site.DefaultContainerType);
} }
else else
{ {
modules = PageState.Modules; modules = PageState.Modules;
} }
_pagestate.Modules = modules;
_pagestate.EditMode = editmode; _pagestate = new PageState
_pagestate.LastSyncDate = lastsyncdate; {
Alias = alias,
Site = site,
Pages = pages,
Page = page,
User = user,
Modules = modules,
Uri = new Uri(_absoluteUri, UriKind.Absolute),
QueryString = querystring,
ModuleId = moduleid,
Action = action,
EditMode = editmode,
LastSyncDate = lastsyncdate,
Runtime = runtime
};
OnStateChange?.Invoke(_pagestate); OnStateChange?.Invoke(_pagestate);
} }
@ -279,7 +277,6 @@
{ {
if (user == null) 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 // redirect to login page
NavigationManager.NavigateTo(Utilities.NavigateUrl(alias.Path, "login", "returnurl=" + path)); NavigationManager.NavigateTo(Utilities.NavigateUrl(alias.Path, "login", "returnurl=" + path));
} }
@ -357,19 +354,36 @@
page.ThemeType = site.DefaultThemeType; page.ThemeType = site.DefaultThemeType;
page.LayoutType = site.DefaultLayoutType; page.LayoutType = site.DefaultLayoutType;
} }
Type type;
page.Panes = new List<string>();
page.Resources = new List<Resource>();
string panes = "";
Type themetype = Type.GetType(page.ThemeType);
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)) if (!string.IsNullOrEmpty(page.LayoutType))
{ {
type = Type.GetType(page.LayoutType); Type layouttype = Type.GetType(page.LayoutType);
} if (layouttype != null)
else
{ {
type = Type.GetType(page.ThemeType); var layoutobject = Activator.CreateInstance(layouttype) as IThemeControl;
if (layoutobject != null)
{
panes = layoutobject.Panes;
}
}
} }
var property = type.GetProperty("Panes"); page.Panes = panes.Replace(";", ",").Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList();
page.Panes = (string)property.GetValue(Activator.CreateInstance(type), null);
} }
catch catch
{ {
@ -379,12 +393,12 @@
return page; return page;
} }
private List<Module> ProcessModules(List<Module> modules, int pageid, int moduleid, string control, string panes, string defaultcontainertype) private (Page Page, List<Module> Modules) ProcessModules(Page page, List<Module> modules, int moduleid, string action, string defaultcontainertype)
{ {
var paneindex = new Dictionary<string, int>(); var paneindex = new Dictionary<string, int>();
foreach (Module module in modules) foreach (Module module in modules)
{ {
if (module.PageId == pageid || module.ModuleId == moduleid) if (module.PageId == page.PageId || module.ModuleId == moduleid)
{ {
var typename = string.Empty; var typename = string.Empty;
if (module.ModuleDefinition != null) if (module.ModuleDefinition != null)
@ -396,63 +410,72 @@
typename = Constants.ErrorModule; typename = Constants.ErrorModule;
} }
if (module.ModuleId == moduleid && control != "") if (module.ModuleId == moduleid && action != "")
{ {
// check if the module defines custom routes // check if the module defines custom routes
if (module.ModuleDefinition.ControlTypeRoutes != "") if (module.ModuleDefinition.ControlTypeRoutes != "")
{ {
foreach (string route in module.ModuleDefinition.ControlTypeRoutes.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)) foreach (string route in module.ModuleDefinition.ControlTypeRoutes.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries))
{ {
if (route.StartsWith(control + "=")) if (route.StartsWith(action + "="))
{ {
typename = route.Replace(control + "=", ""); typename = route.Replace(action + "=", "");
} }
} }
} }
module.ModuleType = typename.Replace(Constants.ActionToken, control); module.ModuleType = typename.Replace(Constants.ActionToken, action);
// admin controls need to load additional metadata from the IModuleControl interface
if (moduleid == module.ModuleId)
{
typename = module.ModuleType;
// check for core module actions component
if (Constants.DefaultModuleActions.Contains(control))
{
typename = Constants.DefaultModuleActionsTemplate.Replace(Constants.ActionToken, control);
}
Type moduletype = Type.GetType(typename);
if (moduletype != null)
{
var moduleobject = Activator.CreateInstance(moduletype);
module.SecurityAccessLevel = (SecurityAccessLevel)moduletype.GetProperty("SecurityAccessLevel").GetValue(moduleobject, null);
module.ControlTitle = (string)moduletype.GetProperty("Title").GetValue(moduleobject);
module.Actions = (string)moduletype.GetProperty("Actions").GetValue(moduleobject);
module.UseAdminContainer = (bool)moduletype.GetProperty("UseAdminContainer").GetValue(moduleobject);
}
}
} }
else else
{ {
module.ModuleType = typename.Replace(Constants.ActionToken, Constants.DefaultAction); module.ModuleType = typename.Replace(Constants.ActionToken, Constants.DefaultAction);
} }
// get additional metadata from IModuleControl interface
typename = module.ModuleType;
if (Constants.DefaultModuleActions.Contains(action))
{
// core framework module action components
typename = Constants.DefaultModuleActionsTemplate.Replace(Constants.ActionToken, action);
}
Type moduletype = Type.GetType(typename);
// ensure component implements IModuleControl
if (moduletype != null && !moduletype.GetInterfaces().Contains(typeof(IModuleControl)))
{
module.ModuleType = "";
}
if (moduletype != null && module.ModuleType != "")
{
var moduleobject = Activator.CreateInstance(moduletype) as IModuleControl;
page.Resources = ManagePageResources(page.Resources, moduleobject.Resources);
// additional metadata needed for admin components
if (module.ModuleId == moduleid && action != "")
{
module.SecurityAccessLevel = moduleobject.SecurityAccessLevel;
module.ControlTitle = moduleobject.Title;
module.Actions = moduleobject.Actions;
module.UseAdminContainer = moduleobject.UseAdminContainer;
}
}
// ensure module's pane exists in current page and if not, assign it to the Admin pane // ensure module's pane exists in current page and if not, assign it to the Admin pane
if (panes == null || !panes.ToLower().Contains(module.Pane.ToLower())) if (page.Panes == null || page.Panes.FindIndex(item => item.Equals(module.Pane, StringComparison.OrdinalIgnoreCase)) == -1)
{ {
module.Pane = Constants.AdminPane; module.Pane = Constants.AdminPane;
} }
// calculate module position within pane // calculate module position within pane
if (paneindex.ContainsKey(module.Pane)) if (paneindex.ContainsKey(module.Pane.ToLower()))
{ {
paneindex[module.Pane] += 1; paneindex[module.Pane.ToLower()] += 1;
} }
else else
{ {
paneindex.Add(module.Pane, 0); paneindex.Add(module.Pane.ToLower(), 0);
} }
module.PaneModuleIndex = paneindex[module.Pane]; module.PaneModuleIndex = paneindex[module.Pane.ToLower()];
if (string.IsNullOrEmpty(module.ContainerType)) if (string.IsNullOrEmpty(module.ContainerType))
{ {
@ -461,16 +484,32 @@
} }
} }
foreach (Module module in modules.Where(item => item.PageId == pageid)) foreach (Module module in modules.Where(item => item.PageId == page.PageId))
{ {
module.PaneModuleCount = paneindex[module.Pane] + 1; module.PaneModuleCount = paneindex[module.Pane.ToLower()] + 1;
} }
return modules; return (page, modules);
}
private List<Resource> ManagePageResources(List<Resource> pageresources, List<Resource> resources)
{
if (resources != null)
{
foreach (var resource in resources)
{
// ensure resource does not exist already
if (pageresources.Find(item => item.Url == resource.Url) == null)
{
pageresources.Add(resource);
}
}
}
return pageresources;
} }
private Runtime GetRuntime() private Runtime GetRuntime()
=> RuntimeInformation.IsOSPlatform(OSPlatform.Create("BROWSER")) => RuntimeInformation.IsOSPlatform(OSPlatform.Create("BROWSER"))
? Runtime.WebAssembly ? Runtime.WebAssembly
: Runtime.Server; : Runtime.Server;
} }

View File

@ -12,6 +12,15 @@
protected override async Task OnParametersSetAsync() protected override async Task OnParametersSetAsync()
{ {
var interop = new Interop(JsRuntime); 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)) if (!string.IsNullOrEmpty(PageState.Page.Title))
{ {
await interop.UpdateTitle(PageState.Page.Title); await interop.UpdateTitle(PageState.Page.Title);
@ -20,10 +29,23 @@
{ {
await interop.UpdateTitle(PageState.Site.Name + " - " + PageState.Page.Name); await interop.UpdateTitle(PageState.Site.Name + " - " + PageState.Page.Name);
} }
// 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))
{
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 = "" });
}
await interop.IncludeLinks(links.ToArray());
await interop.RemoveElementsById("app-stylesheet", "", "app-stylesheet-" + batch + "-00");
// add favicon
if (PageState.Site.FaviconFileId != null) 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) if (PageState.Site.PwaIsEnabled)
{ {
await InitializePwa(interop); await InitializePwa(interop);
@ -32,18 +54,13 @@
DynamicComponent = builder => DynamicComponent = builder =>
{ {
var themeType = Type.GetType(PageState.Page.ThemeType); var themeType = Type.GetType(PageState.Page.ThemeType);
if (themeType != null) if (themeType == null)
{ {
// fallback
themeType = Type.GetType(Constants.DefaultTheme);
}
builder.OpenComponent(0, themeType); builder.OpenComponent(0, themeType);
builder.CloseComponent(); 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();
}
}; };
} }
@ -71,10 +88,10 @@
"const serialized = JSON.stringify(manifest); " + "const serialized = JSON.stringify(manifest); " +
"const blob = new Blob([serialized], {type: 'application/javascript'}); " + "const blob = new Blob([serialized], {type: 'application/javascript'}); " +
"const url = URL.createObjectURL(blob); " + "const url = URL.createObjectURL(blob); " +
"document.getElementById('pwa-manifest').setAttribute('href', url); " + "document.getElementById('app-manifest').setAttribute('href', url); " +
"} " + "} " +
", 1000);"; ", 1000);";
await interop.IncludeScript("pwa-manifestscript", "", manifest, "body", "", ""); await interop.IncludeScript("app-pwa", "", "", "", manifest, "body", "id");
// service worker must be in root of site // service worker must be in root of site
string serviceworker = "if ('serviceWorker' in navigator) { " + string serviceworker = "if ('serviceWorker' in navigator) { " +
@ -84,6 +101,6 @@
"console.log('ServiceWorker Registration Failed ', err); " + "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.1</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.1</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"> <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata> <metadata>
<id>Oqtane.Framework</id> <id>Oqtane.Framework</id>
<version>0.9.1</version> <version>1.0.1</version>
<authors>Shaun Walker</authors> <authors>Shaun Walker</authors>
<owners>.NET Foundation</owners> <owners>.NET Foundation</owners>
<title>Oqtane Framework</title> <title>Oqtane Framework</title>
@ -13,13 +13,16 @@
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl> <projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
<iconUrl>https://www.oqtane.org/Portals/0/icon.jpg</iconUrl> <iconUrl>https://www.oqtane.org/Portals/0/icon.jpg</iconUrl>
<tags>oqtane framework</tags> <tags>oqtane framework</tags>
<releaseNotes>Initial Release</releaseNotes> <releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v1.0.1</releaseNotes>
<summary>A modular application framework for Blazor</summary> <summary>A modular application framework for Blazor</summary>
</metadata> </metadata>
<files> <files>
<file src="..\Oqtane.Client\bin\Release\netstandard2.1\Oqtane.Client.dll" target="lib" /> <file src="..\Oqtane.Client\bin\Release\netstandard2.1\Oqtane.Client.dll" target="lib\netcoreapp3.1" />
<file src="..\Oqtane.Server\bin\Release\netcoreapp3.1\Oqtane.Server.dll" target="lib" /> <file src="..\Oqtane.Client\bin\Release\netstandard2.1\Oqtane.Client.pdb" target="lib\netcoreapp3.1" />
<file src="..\Oqtane.Shared\bin\Release\netstandard2.1\Oqtane.Shared.dll" target="lib" /> <file src="..\Oqtane.Server\bin\Release\netcoreapp3.1\Oqtane.Server.dll" target="lib\netcoreapp3.1" />
<file src="..\Oqtane.Server\bin\Release\netcoreapp3.1\Oqtane.Upgrade.dll" target="lib" /> <file src="..\Oqtane.Server\bin\Release\netcoreapp3.1\Oqtane.Server.pdb" target="lib\netcoreapp3.1" />
<file src="..\Oqtane.Shared\bin\Release\netstandard2.1\Oqtane.Shared.dll" target="lib\netcoreapp3.1" />
<file src="..\Oqtane.Shared\bin\Release\netstandard2.1\Oqtane.Shared.pdb" target="lib\netcoreapp3.1" />
<file src="..\Oqtane.Server\wwwroot\**\*.*" target="wwwroot" />
</files> </files>
</package> </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.1</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.1</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.1</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.1</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

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

View File

@ -0,0 +1,5 @@
DEL "*.nupkg"
dotnet clean -c Release ..\Oqtane.sln
dotnet build -c Release ..\Oqtane.sln
dotnet pack -o .\ -c Release ..\Oqtane.sln
nuget.exe pack Oqtane.Framework.nuspec

View File

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 7.8 KiB

View File

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

View File

@ -16,6 +16,7 @@ using System.Net;
using Oqtane.Enums; using Oqtane.Enums;
using Oqtane.Infrastructure; using Oqtane.Infrastructure;
using Oqtane.Repository; using Oqtane.Repository;
using Microsoft.AspNetCore.Routing.Constraints;
// ReSharper disable StringIndexOfIsCultureSpecific.1 // ReSharper disable StringIndexOfIsCultureSpecific.1
@ -64,7 +65,7 @@ namespace Oqtane.Controllers
{ {
foreach (string file in Directory.GetFiles(folder)) foreach (string file in Directory.GetFiles(folder))
{ {
files.Add(new Models.File {Name = Path.GetFileName(file), Extension = Path.GetExtension(file)?.Replace(".", "")}); files.Add(new Models.File { Name = Path.GetFileName(file), Extension = Path.GetExtension(file)?.Replace(".", "") });
} }
} }
} }
@ -188,14 +189,37 @@ namespace Oqtane.Controllers
{ {
Models.File file = null; Models.File file = null;
Folder folder = _folders.GetFolder(int.Parse(folderid)); Folder folder = _folders.GetFolder(int.Parse(folderid));
if (folder != null && _userPermissions.IsAuthorized(User, PermissionNames.Edit, folder.Permissions))
if (folder == null || !_userPermissions.IsAuthorized(User, PermissionNames.Edit, folder.Permissions))
{ {
_logger.Log(LogLevel.Error, this, LogFunction.Create,
"User Not Authorized To Download File {Url} {FolderId}", url, folderid);
HttpContext.Response.StatusCode = 401;
return file;
}
string folderPath = GetFolderPath(folder); string folderPath = GetFolderPath(folder);
CreateDirectory(folderPath); CreateDirectory(folderPath);
string filename = url.Substring(url.LastIndexOf("/", StringComparison.Ordinal) + 1); string filename = url.Substring(url.LastIndexOf("/", StringComparison.Ordinal) + 1);
// check for allowable file extensions // check for allowable file extensions
if (Constants.UploadableFiles.Contains(Path.GetExtension(filename).Replace(".", ""))) if (!Constants.UploadableFiles.Split(',')
.Contains(Path.GetExtension(filename).ToLower().Replace(".", "")))
{ {
_logger.Log(LogLevel.Error, this, LogFunction.Create,
"File Could Not Be Downloaded From Url Due To Its File Extension {Url}", url);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Conflict;
return file;
}
if (!filename.IsPathOrFileValid())
{
_logger.Log(LogLevel.Error, this, LogFunction.Create,
$"File Could Not Be Downloaded From Url Due To Its File Name Not Allowed {url}");
HttpContext.Response.StatusCode = (int)HttpStatusCode.Conflict;
return file;
}
try try
{ {
var client = new WebClient(); var client = new WebClient();
@ -207,22 +231,12 @@ namespace Oqtane.Controllers
} }
client.DownloadFile(url, targetPath); client.DownloadFile(url, targetPath);
_files.AddFile(CreateFile(filename, folder.FolderId, targetPath)); file = _files.AddFile(CreateFile(filename, folder.FolderId, targetPath));
} }
catch catch
{ {
_logger.Log(LogLevel.Error, this, LogFunction.Create, "File Could Not Be Downloaded From Url {Url}", url); _logger.Log(LogLevel.Error, this, LogFunction.Create,
} "File Could Not Be Downloaded From Url {Url}", url);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Create, "File Could Not Be Downloaded From Url Due To Its File Extension {Url}", url);
}
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Create, "User Not Authorized To Download File {Url} {FolderId}", url, folderid);
HttpContext.Response.StatusCode = 401;
} }
return file; return file;
@ -232,14 +246,24 @@ namespace Oqtane.Controllers
[HttpPost("upload")] [HttpPost("upload")]
public async Task UploadFile(string folder, IFormFile file) public async Task UploadFile(string folder, IFormFile file)
{ {
if (file.Length > 0) if (file.Length <= 0)
{ {
return;
}
if (!file.FileName.IsPathOrFileValid())
{
HttpContext.Response.StatusCode = (int)HttpStatusCode.Conflict;
return;
}
string folderPath = ""; string folderPath = "";
if (int.TryParse(folder, out int folderId)) if (int.TryParse(folder, out int folderId))
{ {
Folder virtualFolder = _folders.GetFolder(folderId); Folder virtualFolder = _folders.GetFolder(folderId);
if (virtualFolder != null && _userPermissions.IsAuthorized(User, PermissionNames.Edit, virtualFolder.Permissions)) if (virtualFolder != null &&
_userPermissions.IsAuthorized(User, PermissionNames.Edit, virtualFolder.Permissions))
{ {
folderPath = GetFolderPath(virtualFolder); folderPath = GetFolderPath(virtualFolder);
} }
@ -268,11 +292,11 @@ namespace Oqtane.Controllers
} }
else else
{ {
_logger.Log(LogLevel.Error, this, LogFunction.Create, "User Not Authorized To Upload File {Folder} {File}", folder, file); _logger.Log(LogLevel.Error, this, LogFunction.Create,
"User Not Authorized To Upload File {Folder} {File}", folder, file);
HttpContext.Response.StatusCode = 401; HttpContext.Response.StatusCode = 401;
} }
} }
}
private async Task<string> MergeFile(string folder, string filename) private async Task<string> MergeFile(string folder, string filename)
{ {
@ -282,7 +306,8 @@ namespace Oqtane.Controllers
string token = ".part_"; string token = ".part_";
string parts = Path.GetExtension(filename)?.Replace(token, ""); // returns "x_y" string parts = Path.GetExtension(filename)?.Replace(token, ""); // returns "x_y"
int totalparts = int.Parse(parts?.Substring(parts.IndexOf("_") + 1)); int totalparts = int.Parse(parts?.Substring(parts.IndexOf("_") + 1));
filename = filename?.Substring(0, filename.IndexOf(token)); // base filename
filename = Path.GetFileNameWithoutExtension(filename); // base filename
string[] fileParts = Directory.GetFiles(folder, filename + token + "*"); // list of all file parts string[] fileParts = Directory.GetFiles(folder, filename + token + "*"); // list of all file parts
// if all of the file parts exist ( note that file parts can arrive out of order ) // if all of the file parts exist ( note that file parts can arrive out of order )
@ -317,7 +342,7 @@ namespace Oqtane.Controllers
} }
// check for allowable file extensions // check for allowable file extensions
if (!Constants.UploadableFiles.Contains(Path.GetExtension(filename)?.Replace(".", ""))) if (!Constants.UploadableFiles.Split(',').Contains(Path.GetExtension(filename)?.ToLower().Replace(".", "")))
{ {
System.IO.File.Delete(Path.Combine(folder, filename + ".tmp")); System.IO.File.Delete(Path.Combine(folder, filename + ".tmp"));
} }
@ -339,13 +364,15 @@ namespace Oqtane.Controllers
} }
// clean up file parts which are more than 2 hours old ( which can happen if a prior file upload failed ) // clean up file parts which are more than 2 hours old ( which can happen if a prior file upload failed )
fileParts = Directory.GetFiles(folder, "*" + token + "*"); var cleanupFiles = Directory.EnumerateFiles(folder, "*" + token + "*")
foreach (string filepart in fileParts) .Where(f => Path.GetExtension(f).StartsWith(token));
foreach (var file in cleanupFiles)
{ {
DateTime createddate = System.IO.File.GetCreationTime(filepart).ToUniversalTime(); var createdDate = System.IO.File.GetCreationTime(file).ToUniversalTime();
if (createddate < DateTime.UtcNow.AddHours(-2)) if (createdDate < DateTime.UtcNow.AddHours(-2))
{ {
System.IO.File.Delete(filepart); System.IO.File.Delete(file);
} }
} }
@ -396,12 +423,13 @@ namespace Oqtane.Controllers
[HttpGet("download/{id}")] [HttpGet("download/{id}")]
public IActionResult Download(int id) public IActionResult Download(int id)
{ {
string errorpath = Path.Combine(GetFolderPath("images"), "error.png");
Models.File file = _files.GetFile(id); Models.File file = _files.GetFile(id);
if (file != null) if (file != null)
{ {
if (_userPermissions.IsAuthorized(User, PermissionNames.View, file.Folder.Permissions)) if (_userPermissions.IsAuthorized(User, PermissionNames.View, file.Folder.Permissions))
{ {
string filepath = Path.Combine(GetFolderPath(file.Folder) , file.Name); string filepath = Path.Combine(GetFolderPath(file.Folder), file.Name);
if (System.IO.File.Exists(filepath)) if (System.IO.File.Exists(filepath))
{ {
byte[] filebytes = System.IO.File.ReadAllBytes(filepath); byte[] filebytes = System.IO.File.ReadAllBytes(filepath);
@ -411,22 +439,29 @@ namespace Oqtane.Controllers
{ {
_logger.Log(LogLevel.Error, this, LogFunction.Read, "File Does Not Exist {FileId} {FilePath}", id, filepath); _logger.Log(LogLevel.Error, this, LogFunction.Read, "File Does Not Exist {FileId} {FilePath}", id, filepath);
HttpContext.Response.StatusCode = 404; HttpContext.Response.StatusCode = 404;
return null; if (System.IO.File.Exists(errorpath))
{
byte[] filebytes = System.IO.File.ReadAllBytes(errorpath);
return File(filebytes, "application/octet-stream", file.Name);
}
} }
} }
else else
{ {
_logger.Log(LogLevel.Error, this, LogFunction.Read, "User Not Authorized To Access File {FileId}", id); _logger.Log(LogLevel.Error, this, LogFunction.Read, "User Not Authorized To Access File {FileId}", id);
HttpContext.Response.StatusCode = 401; HttpContext.Response.StatusCode = 401;
return null; byte[] filebytes = System.IO.File.ReadAllBytes(errorpath);
return File(filebytes, "application/octet-stream", file.Name);
} }
} }
else else
{ {
_logger.Log(LogLevel.Error, this, LogFunction.Read, "File Not Found {FileId}", id); _logger.Log(LogLevel.Error, this, LogFunction.Read, "File Not Found {FileId}", id);
HttpContext.Response.StatusCode = 404; HttpContext.Response.StatusCode = 404;
return null; byte[] filebytes = System.IO.File.ReadAllBytes(errorpath);
return File(filebytes, "application/octet-stream", "error.png");
} }
return null;
} }
private string GetFolderPath(Folder folder) private string GetFolderPath(Folder folder)
@ -448,7 +483,7 @@ namespace Oqtane.Controllers
string[] folders = folderpath.Split(separators, StringSplitOptions.RemoveEmptyEntries); string[] folders = folderpath.Split(separators, StringSplitOptions.RemoveEmptyEntries);
foreach (string folder in folders) foreach (string folder in folders)
{ {
path = Utilities.PathCombine(path, folder,"\\"); path = Utilities.PathCombine(path, folder, "\\");
if (!Directory.Exists(path)) if (!Directory.Exists(path))
{ {
Directory.CreateDirectory(path); Directory.CreateDirectory(path);
@ -465,11 +500,11 @@ namespace Oqtane.Controllers
FileInfo fileinfo = new FileInfo(filepath); FileInfo fileinfo = new FileInfo(filepath);
file.Extension = fileinfo.Extension.ToLower().Replace(".", ""); file.Extension = fileinfo.Extension.ToLower().Replace(".", "");
file.Size = (int) fileinfo.Length; file.Size = (int)fileinfo.Length;
file.ImageHeight = 0; file.ImageHeight = 0;
file.ImageWidth = 0; file.ImageWidth = 0;
if (Constants.ImageFiles.Contains(file.Extension)) if (Constants.ImageFiles.Split(',').Contains(file.Extension.ToLower()))
{ {
FileStream stream = new FileStream(filepath, FileMode.Open, FileAccess.Read); FileStream stream = new FileStream(filepath, FileMode.Open, FileAccess.Read);
using (var image = Image.FromStream(stream)) using (var image = Image.FromStream(stream))

View File

@ -10,7 +10,6 @@ using Oqtane.Extensions;
using Oqtane.Infrastructure; using Oqtane.Infrastructure;
using Oqtane.Repository; using Oqtane.Repository;
using Oqtane.Security; using Oqtane.Security;
using System.IO;
namespace Oqtane.Controllers namespace Oqtane.Controllers
{ {
@ -33,7 +32,7 @@ namespace Oqtane.Controllers
public IEnumerable<Folder> Get(string siteid) public IEnumerable<Folder> Get(string siteid)
{ {
List<Folder> folders = new List<Folder>(); List<Folder> folders = new List<Folder>();
foreach(Folder folder in _folders.GetFolders(int.Parse(siteid))) foreach (Folder folder in _folders.GetFolders(int.Parse(siteid)))
{ {
if (_userPermissions.IsAuthorized(User, PermissionNames.Browse, folder.Permissions)) if (_userPermissions.IsAuthorized(User, PermissionNames.Browse, folder.Permissions))
{ {
@ -104,17 +103,27 @@ namespace Oqtane.Controllers
new Permission(PermissionNames.Edit, Constants.AdminRole, true), new Permission(PermissionNames.Edit, Constants.AdminRole, true),
}.EncodePermissions(); }.EncodePermissions();
} }
if (_userPermissions.IsAuthorized(User,PermissionNames.Edit, permissions)) if (_userPermissions.IsAuthorized(User, PermissionNames.Edit, permissions))
{
if (folder.IsPathValid())
{ {
if (string.IsNullOrEmpty(folder.Path) && folder.ParentId != null) if (string.IsNullOrEmpty(folder.Path) && folder.ParentId != null)
{ {
Folder parent = _folders.GetFolder(folder.ParentId.Value); Folder parent = _folders.GetFolder(folder.ParentId.Value);
folder.Path = Utilities.PathCombine(parent.Path, folder.Name,"\\"); folder.Path = Utilities.PathCombine(parent.Path, folder.Name);
} }
folder.Path = Utilities.PathCombine(folder.Path, "\\");
folder = _folders.AddFolder(folder); folder = _folders.AddFolder(folder);
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Folder Added {Folder}", folder); _logger.Log(LogLevel.Information, this, LogFunction.Create, "Folder Added {Folder}", folder);
} }
else else
{
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Folder Name Not Valid {Folder}", folder);
HttpContext.Response.StatusCode = 401;
folder = null;
}
}
else
{ {
_logger.Log(LogLevel.Error, this, LogFunction.Create, "User Not Authorized To Add Folder {Folder}", folder); _logger.Log(LogLevel.Error, this, LogFunction.Create, "User Not Authorized To Add Folder {Folder}", folder);
HttpContext.Response.StatusCode = 401; HttpContext.Response.StatusCode = 401;
@ -130,16 +139,26 @@ namespace Oqtane.Controllers
public Folder Put(int id, [FromBody] Folder folder) public Folder Put(int id, [FromBody] Folder folder)
{ {
if (ModelState.IsValid && _userPermissions.IsAuthorized(User, EntityNames.Folder, folder.FolderId, PermissionNames.Edit)) if (ModelState.IsValid && _userPermissions.IsAuthorized(User, EntityNames.Folder, folder.FolderId, PermissionNames.Edit))
{
if (folder.IsPathValid())
{ {
if (string.IsNullOrEmpty(folder.Path) && folder.ParentId != null) if (string.IsNullOrEmpty(folder.Path) && folder.ParentId != null)
{ {
Folder parent = _folders.GetFolder(folder.ParentId.Value); Folder parent = _folders.GetFolder(folder.ParentId.Value);
folder.Path = Utilities.PathCombine(parent.Path, folder.Name,"\\"); folder.Path = Utilities.PathCombine(parent.Path, folder.Name);
} }
folder.Path = Utilities.PathCombine(folder.Path, "\\");
folder = _folders.UpdateFolder(folder); folder = _folders.UpdateFolder(folder);
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Folder Updated {Folder}", folder); _logger.Log(LogLevel.Information, this, LogFunction.Update, "Folder Updated {Folder}", folder);
} }
else else
{
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Folder Name Not Valid {Folder}", folder);
HttpContext.Response.StatusCode = 401;
folder = null;
}
}
else
{ {
_logger.Log(LogLevel.Error, this, LogFunction.Update, "User Not Authorized To Update Folder {Folder}", folder); _logger.Log(LogLevel.Error, this, LogFunction.Update, "User Not Authorized To Update Folder {Folder}", folder);
HttpContext.Response.StatusCode = 401; HttpContext.Response.StatusCode = 401;

View File

@ -4,6 +4,14 @@ using Microsoft.Extensions.Configuration;
using Oqtane.Models; using Oqtane.Models;
using Oqtane.Shared; using Oqtane.Shared;
using Oqtane.Infrastructure; using Oqtane.Infrastructure;
using System;
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 namespace Oqtane.Controllers
{ {
@ -55,5 +63,76 @@ namespace Oqtane.Controllers
_installationManager.UpgradeFramework(); _installationManager.UpgradeFramework();
return installation; return installation;
} }
// GET api/<controller>/load
[HttpGet("load")]
public IActionResult Load()
{
if (_config.GetSection("Runtime").Value == "WebAssembly")
{
// get list of assemblies which should be downloaded to browser
var assemblies = AppDomain.CurrentDomain.GetOqtaneClientAssemblies();
var list = assemblies.Select(a => a.GetName().Name).ToList();
// 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);
byte[] zipfile;
using (var memoryStream = new MemoryStream())
{
using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
{
ZipArchiveEntry entry;
foreach (string file in list)
{
entry = archive.CreateEntry(file + ".dll");
using (var filestream = new FileStream(Path.Combine(binfolder, file + ".dll"), FileMode.Open, FileAccess.Read))
using (var entrystream = entry.Open())
{
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");
using (var filestream = new FileStream(Path.Combine(binfolder, file + ".pdb"), FileMode.Open, FileAccess.Read))
using (var entrystream = entry.Open())
{
filestream.CopyTo(entrystream);
}
}
}
}
zipfile = memoryStream.ToArray();
}
return File(zipfile, "application/octet-stream", "oqtane.zip");
}
else
{
HttpContext.Response.StatusCode = 401;
return null;
}
}
} }
} }

View File

@ -13,7 +13,8 @@ using Oqtane.Repository;
using Oqtane.Security; using Oqtane.Security;
using System; using System;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
// ReSharper disable StringIndexOfIsCultureSpecific.1 using Microsoft.Extensions.Configuration;
using System.Xml.Linq;
namespace Oqtane.Controllers namespace Oqtane.Controllers
{ {
@ -23,20 +24,24 @@ namespace Oqtane.Controllers
private readonly IModuleDefinitionRepository _moduleDefinitions; private readonly IModuleDefinitionRepository _moduleDefinitions;
private readonly IModuleRepository _modules; private readonly IModuleRepository _modules;
private readonly ITenantRepository _tenants; private readonly ITenantRepository _tenants;
private readonly ISqlRepository _sql;
private readonly IUserPermissions _userPermissions; private readonly IUserPermissions _userPermissions;
private readonly IInstallationManager _installationManager; private readonly IInstallationManager _installationManager;
private readonly IWebHostEnvironment _environment; private readonly IWebHostEnvironment _environment;
private readonly IConfigurationRoot _config;
private readonly IServiceProvider _serviceProvider; private readonly IServiceProvider _serviceProvider;
private readonly ILogManager _logger; private readonly ILogManager _logger;
public ModuleDefinitionController(IModuleDefinitionRepository moduleDefinitions, IModuleRepository modules,ITenantRepository tenants, IUserPermissions userPermissions, IInstallationManager installationManager, IWebHostEnvironment environment, IServiceProvider serviceProvider, ILogManager logger) public ModuleDefinitionController(IModuleDefinitionRepository moduleDefinitions, IModuleRepository modules,ITenantRepository tenants, ISqlRepository sql, IUserPermissions userPermissions, IInstallationManager installationManager, IWebHostEnvironment environment, IConfigurationRoot config, IServiceProvider serviceProvider, ILogManager logger)
{ {
_moduleDefinitions = moduleDefinitions; _moduleDefinitions = moduleDefinitions;
_modules = modules; _modules = modules;
_tenants = tenants; _tenants = tenants;
_sql = sql;
_userPermissions = userPermissions; _userPermissions = userPermissions;
_installationManager = installationManager; _installationManager = installationManager;
_environment = environment; _environment = environment;
_config = config;
_serviceProvider = serviceProvider; _serviceProvider = serviceProvider;
_logger = logger; _logger = logger;
} }
@ -99,76 +104,61 @@ namespace Oqtane.Controllers
public void Delete(int id, int siteid) public void Delete(int id, int siteid)
{ {
ModuleDefinition moduledefinition = _moduleDefinitions.GetModuleDefinition(id, siteid); ModuleDefinition moduledefinition = _moduleDefinitions.GetModuleDefinition(id, siteid);
if (moduledefinition != null) if (moduledefinition != null )
{ {
if (!string.IsNullOrEmpty(moduledefinition.ServerManagerType)) if (!string.IsNullOrEmpty(moduledefinition.ServerManagerType) && Utilities.GetAssemblyName(moduledefinition.ServerManagerType) != "Oqtane.Server")
{ {
Type moduletype = Type.GetType(moduledefinition.ServerManagerType); Type moduletype = Type.GetType(moduledefinition.ServerManagerType);
if (moduletype != null && moduletype.GetInterface("IInstallable") != null)
{
foreach (Tenant tenant in _tenants.GetTenants()) foreach (Tenant tenant in _tenants.GetTenants())
{ {
try try
{
if (moduletype.GetInterface("IInstallable") != null)
{ {
var moduleobject = ActivatorUtilities.CreateInstance(_serviceProvider, moduletype); var moduleobject = ActivatorUtilities.CreateInstance(_serviceProvider, moduletype);
((IInstallable)moduleobject).Uninstall(tenant); ((IInstallable)moduleobject).Uninstall(tenant);
} }
catch else
{ {
// an error occurred executing the uninstall _sql.ExecuteScript(tenant, moduletype.Assembly, Utilities.GetTypeName(moduledefinition.ModuleDefinitionName) + ".Uninstall.sql");
} }
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "{ModuleDefinitionName} Uninstalled For Tenant {Tenant}", moduledefinition.ModuleDefinitionName, tenant.Name);
} }
} catch (Exception ex)
}
// format root assembly name
string assemblyname = Utilities.GetAssemblyName(moduledefinition.ModuleDefinitionName);
if (assemblyname != "Oqtane.Client")
{ {
assemblyname = assemblyname.Replace(".Client", ""); _logger.Log(LogLevel.Error, this, LogFunction.Delete, "Error Uninstalling {ModuleDefinitionName} For Tenant {Tenant} {Error}", moduledefinition.ModuleDefinitionName, tenant.Name, ex.Message);
}
}
// clean up module static resource folder // clean up module static resource folder
string folder = Path.Combine(_environment.WebRootPath, Path.Combine("Modules",assemblyname)); string folder = Path.Combine(_environment.WebRootPath, Path.Combine("Modules", Utilities.GetTypeName(moduledefinition.ModuleDefinitionName)));
if (Directory.Exists(folder)) if (Directory.Exists(folder))
{ {
Directory.Delete(folder, true); Directory.Delete(folder, true);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Static Resources Removed For {AssemblynName}", assemblyname); _logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Static Resources Removed For {ModuleDefinitionName}", moduledefinition.ModuleDefinitionName);
} }
// remove module assembly from /bin // get root assembly name ( note that this only works if modules follow a specific naming convention for their assemblies )
string assemblyname = Utilities.GetAssemblyName(moduledefinition.ModuleDefinitionName).ToLower();
assemblyname = assemblyname.Replace(".client", "").Replace(".oqtane", "");
// remove module assemblies from /bin
string binfolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); string binfolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
foreach (string file in Directory.EnumerateFiles(binfolder, assemblyname + "*.*")) foreach (string file in Directory.EnumerateFiles(binfolder, assemblyname + "*.*"))
{ {
System.IO.File.Delete(file); System.IO.File.Delete(file);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Assembly Removed {Filename}", file); _logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Assembly {Filename} Removed For {ModuleDefinitionName}", file, moduledefinition.ModuleDefinitionName);
}
} }
// remove module definition // remove module definition
_moduleDefinitions.DeleteModuleDefinition(id, siteid); _moduleDefinitions.DeleteModuleDefinition(id, siteid);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Definition Deleted {ModuleDefinitionName}", moduledefinition.Name); _logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Definition {ModuleDefinitionName} Deleted", moduledefinition.Name);
// restart application // restart application
_installationManager.RestartApplication(); _installationManager.RestartApplication();
} }
} }
// GET api/<controller>/load/assembyname
[HttpGet("load/{assemblyname}")]
public IActionResult Load(string assemblyname)
{
if (Path.GetExtension(assemblyname).ToLower() == ".dll")
{
string binfolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
byte[] file = System.IO.File.ReadAllBytes(Path.Combine(binfolder, assemblyname));
return File(file, "application/octet-stream", assemblyname);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Read, "User Not Authorized To Download Assembly {Assembly}", assemblyname);
HttpContext.Response.StatusCode = 401;
return null;
}
} }
// POST api/<controller>?moduleid=x // POST api/<controller>?moduleid=x
@ -180,18 +170,18 @@ namespace Oqtane.Controllers
{ {
string rootPath; string rootPath;
DirectoryInfo rootFolder = Directory.GetParent(_environment.ContentRootPath); DirectoryInfo rootFolder = Directory.GetParent(_environment.ContentRootPath);
string templatePath = Utilities.PathCombine(rootFolder.FullName, "Oqtane.Client", "Modules", "Admin", "ModuleCreator", "Templates",moduleDefinition.Template,"\\"); string templatePath = Utilities.PathCombine(_environment.WebRootPath, "Modules", "Templates", moduleDefinition.Template,"\\");
if (moduleDefinition.Template == "internal") if (moduleDefinition.Template == "internal")
{ {
rootPath = Utilities.PathCombine(rootFolder.FullName,"\\"); rootPath = Utilities.PathCombine(rootFolder.FullName,"\\");
moduleDefinition.ModuleDefinitionName = moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Modules, Oqtane.Client"; moduleDefinition.ModuleDefinitionName = moduleDefinition.Owner + "." + moduleDefinition.Name + "s, Oqtane.Client";
moduleDefinition.ServerManagerType = moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Manager." + moduleDefinition.Name + "Manager, Oqtane.Server"; moduleDefinition.ServerManagerType = moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Manager." + moduleDefinition.Name + "Manager, Oqtane.Server";
} }
else else
{ {
rootPath = Utilities.PathCombine(rootFolder.Parent.FullName , moduleDefinition.Owner + "." + moduleDefinition.Name + "s","\\"); rootPath = Utilities.PathCombine(rootFolder.Parent.FullName , moduleDefinition.Owner + "." + moduleDefinition.Name + "s","\\");
moduleDefinition.ModuleDefinitionName = moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Modules, " + moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Client.Oqtane"; moduleDefinition.ModuleDefinitionName = moduleDefinition.Owner + "." + moduleDefinition.Name + "s, " + moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Client.Oqtane";
moduleDefinition.ServerManagerType = moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Manager." + moduleDefinition.Name + "Manager, " + moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Server.Oqtane"; moduleDefinition.ServerManagerType = moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Manager." + moduleDefinition.Name + "Manager, " + moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Server.Oqtane";
} }
@ -204,7 +194,11 @@ namespace Oqtane.Controllers
if (moduleDefinition.Template == "internal") if (moduleDefinition.Template == "internal")
{ {
// need logic to add embedded scripts to Oqtane.Server.csproj - also you need to remove them on uninstall // add embedded resources to project
List<string> resources = new List<string>();
resources.Add(Utilities.PathCombine("Modules", moduleDefinition.Owner + "." + moduleDefinition.Name + "s", "Scripts", moduleDefinition.Owner + "." + moduleDefinition.Name + "s.1.0.0.sql"));
resources.Add(Utilities.PathCombine("Modules", moduleDefinition.Owner + "." + moduleDefinition.Name + "s", "Scripts", moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Uninstall.sql"));
EmbedResourceFiles(Utilities.PathCombine(rootPath, "Oqtane.Server", "Oqtane.Server.csproj"), resources);
} }
_installationManager.RestartApplication(); _installationManager.RestartApplication();
@ -253,5 +247,19 @@ namespace Oqtane.Controllers
} }
} }
} }
private void EmbedResourceFiles(string projectfile, List<string> resources)
{
XDocument project = XDocument.Load(projectfile);
var itemGroup = project.Descendants("ItemGroup").Descendants("EmbeddedResource").FirstOrDefault().Parent;
if (itemGroup != null)
{
foreach (var resource in resources)
{
itemGroup.Add(new XElement("EmbeddedResource", new XAttribute("Include", resource)));
}
}
project.Save(projectfile);
}
} }
} }

View File

@ -166,10 +166,11 @@ namespace Oqtane.Controllers
page.EditMode = false; page.EditMode = false;
page.ThemeType = parent.ThemeType; page.ThemeType = parent.ThemeType;
page.LayoutType = parent.LayoutType; page.LayoutType = parent.LayoutType;
page.DefaultContainerType = parent.DefaultContainerType;
page.Icon = parent.Icon; page.Icon = parent.Icon;
page.Permissions = new List<Permission> { page.Permissions = new List<Permission> {
new Permission(PermissionNames.View, userid, true), new Permission(PermissionNames.View, int.Parse(userid), true),
new Permission(PermissionNames.Edit, userid, true) new Permission(PermissionNames.Edit, int.Parse(userid), true)
}.EncodePermissions(); }.EncodePermissions();
page.IsPersonalizable = false; page.IsPersonalizable = false;
page.UserId = int.Parse(userid); page.UserId = int.Parse(userid);
@ -186,8 +187,8 @@ namespace Oqtane.Controllers
module.ModuleDefinitionName = pm.Module.ModuleDefinitionName; module.ModuleDefinitionName = pm.Module.ModuleDefinitionName;
module.AllPages = false; module.AllPages = false;
module.Permissions = new List<Permission> { module.Permissions = new List<Permission> {
new Permission(PermissionNames.View, userid, true), new Permission(PermissionNames.View, int.Parse(userid), true),
new Permission(PermissionNames.Edit, userid, true) new Permission(PermissionNames.Edit, int.Parse(userid), true)
}.EncodePermissions(); }.EncodePermissions();
module = _modules.AddModule(module); module = _modules.AddModule(module);

View File

@ -1,7 +1,9 @@
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Oqtane.Models; using Oqtane.Models;
using Oqtane.Repository; using Oqtane.Repository;
using Oqtane.Shared;
namespace Oqtane.Controllers namespace Oqtane.Controllers
{ {
@ -17,6 +19,7 @@ namespace Oqtane.Controllers
// GET: api/<controller> // GET: api/<controller>
[HttpGet] [HttpGet]
[Authorize(Roles = Constants.HostRole)]
public IEnumerable<SiteTemplate> Get() public IEnumerable<SiteTemplate> Get()
{ {
return _siteTemplates.GetSiteTemplates(); return _siteTemplates.GetSiteTemplates();

View File

@ -54,22 +54,20 @@ namespace Oqtane.Controllers
{ {
List<Theme> themes = _themes.GetThemes().ToList(); List<Theme> themes = _themes.GetThemes().ToList();
Theme theme = themes.Where(item => item.ThemeName == themename).FirstOrDefault(); Theme theme = themes.Where(item => item.ThemeName == themename).FirstOrDefault();
if (theme != null) if (theme != null && Utilities.GetAssemblyName(theme.ThemeName) != "Oqtane.Client")
{ {
themename = theme.ThemeName.Substring(0, theme.ThemeName.IndexOf(",")); // clean up theme static resource folder
string folder = Path.Combine(_environment.WebRootPath, "Themes" , Utilities.GetTypeName(theme.ThemeName));
string folder = Path.Combine(_environment.WebRootPath, "Themes" , themename);
if (Directory.Exists(folder)) if (Directory.Exists(folder))
{ {
Directory.Delete(folder, true); Directory.Delete(folder, true);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Theme Static Resources Removed For {ThemeName}", theme.ThemeName);
} }
// remove theme assembly from /bin
string binfolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); string binfolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
foreach (string file in Directory.EnumerateFiles(binfolder, themename + "*.dll")) System.IO.File.Delete(Path.Combine(binfolder, Utilities.GetAssemblyName(theme.ThemeName) + ".dll"));
{ _logger.Log(LogLevel.Information, this, LogFunction.Delete, "Theme Assembly {Filename} Removed For {ThemeName}", Utilities.GetAssemblyName(theme.ThemeName) + ".dll", themename);
System.IO.File.Delete(file);
}
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Theme Deleted {ThemeName}", themename);
_installationManager.RestartApplication(); _installationManager.RestartApplication();
} }

View File

@ -57,7 +57,7 @@ namespace Oqtane.Controllers
user.SiteId = int.Parse(siteid); user.SiteId = int.Parse(siteid);
user.Roles = GetUserRoles(user.UserId, user.SiteId); user.Roles = GetUserRoles(user.UserId, user.SiteId);
} }
return user; return Filter(user);
} }
// GET api/<controller>/name/x?siteid=x // GET api/<controller>/name/x?siteid=x
@ -70,6 +70,29 @@ namespace Oqtane.Controllers
user.SiteId = int.Parse(siteid); user.SiteId = int.Parse(siteid);
user.Roles = GetUserRoles(user.UserId, user.SiteId); user.Roles = GetUserRoles(user.UserId, user.SiteId);
} }
return Filter(user);
}
private User Filter(User user)
{
if (user != null && !User.IsInRole(Constants.AdminRole) && User.Identity.Name?.ToLower() != user.Username.ToLower())
{
user.DisplayName = "";
user.Email = "";
user.PhotoFileId = null;
user.LastLoginOn = DateTime.MinValue;
user.LastIPAddress = "";
user.Roles = "";
user.CreatedBy = "";
user.CreatedOn = DateTime.MinValue;
user.ModifiedBy = "";
user.ModifiedOn = DateTime.MinValue;
user.DeletedBy = "";
user.DeletedOn = DateTime.MinValue;
user.IsDeleted = false;
user.Password = "";
user.IsAuthenticated = false;
}
return user; return user;
} }
@ -125,7 +148,7 @@ namespace Oqtane.Controllers
notification.SiteId = user.SiteId; notification.SiteId = user.SiteId;
notification.FromUserId = null; notification.FromUserId = null;
notification.ToUserId = newUser.UserId; notification.ToUserId = newUser.UserId;
notification.ToEmail = ""; notification.ToEmail = newUser.Email;
notification.Subject = "User Account Verification"; notification.Subject = "User Account Verification";
string token = await _identityUserManager.GenerateEmailConfirmationTokenAsync(identityuser); string token = await _identityUserManager.GenerateEmailConfirmationTokenAsync(identityuser);
string url = HttpContext.Request.Scheme + "://" + _tenants.GetAlias().Name + "/login?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token); string url = HttpContext.Request.Scheme + "://" + _tenants.GetAlias().Name + "/login?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token);
@ -134,6 +157,7 @@ namespace Oqtane.Controllers
notification.CreatedOn = DateTime.UtcNow; notification.CreatedOn = DateTime.UtcNow;
notification.IsDelivered = false; notification.IsDelivered = false;
notification.DeliveredOn = null; notification.DeliveredOn = null;
notification.SendOn = DateTime.UtcNow;
_notifications.AddNotification(notification); _notifications.AddNotification(notification);
} }
@ -362,6 +386,7 @@ namespace Oqtane.Controllers
notification.CreatedOn = DateTime.UtcNow; notification.CreatedOn = DateTime.UtcNow;
notification.IsDelivered = false; notification.IsDelivered = false;
notification.DeliveredOn = null; notification.DeliveredOn = null;
notification.SendOn = DateTime.UtcNow;
_notifications.AddNotification(notification); _notifications.AddNotification(notification);
_logger.Log(LogLevel.Information, this, LogFunction.Security, "Password Reset Notification Sent For {Username}", user.Username); _logger.Log(LogLevel.Information, this, LogFunction.Security, "Password Reset Notification Sent For {Username}", user.Username);
} }

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