Compare commits

..

738 Commits

Author SHA1 Message Date
d8a65e9422 Merge pull request #1252 from oqtane/dev
2.0.2 release
2021-04-19 09:24:01 -04:00
da3b83acf8 Merge pull request #1251 from sbwalker/dev
2.0.2 release testing
2021-04-19 08:38:08 -04:00
a85376afb1 2.0.2 release testing 2021-04-19 08:40:58 -04:00
0fe931d772 prepare for 2.0.2 release 2021-04-19 08:04:33 -04:00
2c69392f43 Merge pull request #1248 from sbwalker/dev
prepare for 2.0.2 release
2021-04-19 08:01:50 -04:00
3b54d17286 Merge pull request #89 from oqtane/dev
sync
2021-04-18 14:10:17 -04:00
7a3b2e6dfc Merge pull request #1246 from leigh-pointer/dev.fork
Redirect on log out #1225 Fix
2021-04-18 14:07:55 -04:00
82b85ab24d optimize GetSupportedCultures 2021-04-18 11:51:33 -04:00
8899504d05 Merge pull request #1247 from sbwalker/dev
optimize GetSupportedCultures
2021-04-18 11:48:59 -04:00
1e7e2c8848 Redirect on log out #1225 Fix 2021-04-18 16:58:58 +02:00
cbe843bafc User experience improvements 2021-04-17 19:18:24 -04:00
8dac6fab54 Merge pull request #1244 from sbwalker/dev
User experience improvements
2021-04-17 19:16:13 -04:00
1d3a79437c add theme creator module to simplify the scaffolding of external themes 2021-04-07 13:05:18 -04:00
947c254e12 Merge pull request #1230 from sbwalker/dev
add theme creator modules to simplify the scaffolding of external themes
2021-04-07 13:03:04 -04:00
d05747af1e enhanced ModuleActions component to display panes in a submenu, added more containers to Oqtane theme, added more panes to MultiPane layout, added module outline in edit mode to distinguish modules in panes, consolidated to use a single default AdminPane named "Content", fixed bug related to custom Admin Container behavior 2021-04-06 17:45:11 -04:00
0bf85a7a14 Merge pull request #1228 from sbwalker/dev
enhanced ModuleActions component to display panes in a submenu, added more containers to Oqtane theme, added more panes to MultiPane layout, added module outline in edit mode to distinguish modules in panes, consolidated to use a single default AdminPane named "Content", fixed bug related to custom Admin Container behavior
2021-04-06 17:42:43 -04:00
6b90e0f37b fix #1197 - move folder path logic from Client Service to Server Controller 2021-04-05 08:53:22 -04:00
224f0c3ceb Merge pull request #1227 from sbwalker/dev
fix #1197 - move folder path logic from Client Service to Server Controller
2021-04-05 08:52:29 -04:00
fc145a167d organize theme components into folders 2021-04-03 19:46:29 -04:00
6354c9ebd0 Merge pull request #1226 from sbwalker/dev
organize theme components into folders
2021-04-03 19:43:49 -04:00
61b73060e5 login form validation 2021-04-02 16:14:02 -04:00
b5c1184fa4 Merge pull request #1224 from sbwalker/dev
login form validation
2021-04-02 16:11:38 -04:00
6606ebb58a Merge pull request #88 from oqtane/dev
sync
2021-04-02 13:19:36 -04:00
14480edd67 added globally unique identifier for Site ( used string data type to ensure compatibility with multiple database engines ) 2021-04-02 12:30:20 -04:00
d990a9fc30 Merge pull request #1221 from sbwalker/dev
added globally unique identifier for Site ( used string data type to ensure compatibility with multiple database engines )
2021-04-02 12:27:43 -04:00
00f8f2cb89 fix #1211 - add message to indicate Module Creator is only intended for use in development environments 2021-04-02 08:37:37 -04:00
57575945cd Merge pull request #1219 from sbwalker/dev
fix #1211 - add message to indicate Module Creator is only intended for use in development environments
2021-04-02 08:35:04 -04:00
9995066628 Merge pull request #1216 from leigh-pointer/dev.fork
Added Keypress event to catch Enter key on Login page #1198
2021-04-02 08:20:42 -04:00
c794f77d65 fix #1217 - ensure parent page cannot be set to current page 2021-04-02 08:19:55 -04:00
6d443842b2 Merge pull request #1218 from sbwalker/dev
fix #1217 - ensure parent page cannot be set to current page
2021-04-02 08:17:17 -04:00
60a1f4ed15 Added functionality, when the EnterKey is press the login command is run. 2021-04-02 07:01:27 +02:00
59e5eabeb8 Merge pull request #3 from oqtane/dev
Pull from oqtane.dev
2021-04-02 06:43:44 +02:00
af5f79d343 fix dropdown list UX behavior where there is a default option 2021-04-01 17:58:59 -04:00
39d0afa321 Merge pull request #1215 from sbwalker/dev
fix dropdown list UX behavior where there is a default option
2021-04-01 17:56:29 -04:00
0a7fe5d82e Merge pull request #2 from oqtane/dev
Pull from source
2021-04-01 16:45:16 +02:00
58c84da9c9 add ability to test SMTP connection in Site Settings 2021-04-01 09:44:07 -04:00
46219df6b6 Merge pull request #1213 from sbwalker/dev
add ability to test SMTP connection in Site Settings
2021-04-01 09:41:45 -04:00
49f21e235b Merge pull request #87 from oqtane/dev
sync
2021-03-31 15:37:15 -04:00
bd48e1d8f1 if running on WebAssembly reload the client application if the server application is restarted 2021-03-31 15:39:01 -04:00
710d1be64b Merge pull request #1212 from sbwalker/dev
if running on WebAssembly reload the client application if the server application is restarted
2021-03-31 15:36:32 -04:00
bde1ff44f8 Merge pull request #1206 from hishamco/localizer
Fix localizer in Admin pages
2021-03-31 10:16:53 -04:00
c92a81fcb6 Remove unnecessary localizer from RecycleBin page 2021-03-31 14:15:36 +03:00
ec0b317f80 Fix localizer in Admin pages 2021-03-31 00:50:19 +03:00
09c040128a Ensure Install Wizard will only be displayed if the Master database connection string in appsettings.json is not specified. This addresses a potential security issue where the Install Wizard could be displayed in an existing installation if the Master database connection failed during startup. 2021-03-30 17:48:49 -04:00
c037614917 Merge pull request #1205 from sbwalker/dev
Ensure Install Wizard will only be displayed if the Master database connection string in appsettings.json is not specified. This addresses a potential security issue where the Install Wizard could be displayed in an existing installation if the Master database connection failed during startup.
2021-03-30 17:46:42 -04:00
3c5e7f997b Merge pull request #1204 from hishamco/typo
thene -> theme
2021-03-30 15:50:56 -04:00
367a23171d thene -> theme 2021-03-30 17:26:03 +03:00
0e20cad2d1 Merge pull request #1 from oqtane/dev
pull from source dev
2021-03-30 16:22:55 +02:00
5cd1d3a7af Merge pull request #86 from oqtane/dev
sync
2021-03-30 10:05:35 -04:00
62362b9194 make module creator templates extensible 2021-03-30 10:06:25 -04:00
40501c6ebd Merge pull request #1202 from hishamco/resources
Add Resources folder to Oqtane.Client
2021-03-30 10:04:38 -04:00
82b8c86fe3 Merge pull request #1203 from sbwalker/dev
make module creator templates extensible
2021-03-30 10:03:56 -04:00
40f9437ea5 Add Resources folder to Oqtane.Client 2021-03-29 20:15:04 +03:00
d8bcc32239 refactor user deletion 2021-03-29 12:58:40 -04:00
77694da100 Merge pull request #1201 from sbwalker/dev
refactor user deletion
2021-03-29 12:56:18 -04:00
7223952eb2 Merge pull request #85 from oqtane/dev
sync
2021-03-25 13:49:47 -04:00
1271064872 Merge pull request #1178 from Raceeend/user-delete
fix: user delete
2021-03-25 13:49:11 -04:00
095d2e5f45 Merge pull request #1188 from erw13n/dev
Remove admin border after edit
2021-03-25 08:33:06 -04:00
4152b5c360 Merge pull request #1177 from hishamco/profile
Delete profile should refresh profiles list
2021-03-25 08:12:53 -04:00
63140bce81 Remove admin border after edit
After finish Edit, there's a class "container" which is conflict with Bootstrap that cause an issue on Full-width pane.
2021-03-17 19:26:50 +07:00
c527f28a6d Address feedback 2021-03-13 17:02:24 +03:00
801615c981 fix: user delete 2021-03-13 12:34:47 +01:00
c5e3c9b35e Delete profile should refresh profiles list 2021-03-13 13:02:40 +03:00
86ce8994d9 fix #1156 add defensive coding for scenario where host name does not match any alias 2021-03-11 20:21:15 -05:00
51687c382d Merge pull request #1174 from sbwalker/dev
fix #1156 add defensive coding for scenario where host name does not match any alias
2021-03-11 20:19:07 -05:00
6d3e17a5f5 Fix Pager component issue which manifested itself in Event Log. This reverts a fix from #1160 and addresses the root problem. 2021-03-11 11:21:47 -05:00
32eaa90e5e Merge pull request #1171 from sbwalker/dev
Fix Pager component issue which manifested itself in Event Log. This reverts a fix from #1160 and addresses the root problem.
2021-03-11 11:19:58 -05:00
ed12194ea2 Merge pull request #84 from oqtane/dev
sync
2021-03-10 17:01:33 -05:00
6bde83fab1 Merge pull request #1163 from leigh-pointer/dev
Issue with menu Icon Spacing. #1160
2021-03-10 17:00:08 -05:00
b58c7386b3 Fixed issue Event Log strange behavior with the Paging control #1158 2021-03-10 17:00:49 +01:00
3ff1dc4a21 MenuItems updated with HTML markup in place of the FontIcon component 2021-03-09 17:16:25 +01:00
78aabaafb3 Merge pull request #11 from oqtane/dev
Merge from source
2021-03-09 15:50:08 +01:00
81b76f03ee Add folder structure to external module template client project to organize code and emphasize that a module project can contain multiple modules. 2021-03-09 09:47:01 -05:00
c9fa022279 Merge pull request #1161 from hishamco/fix#1152
Rows -> Maximum Records
2021-03-09 09:46:35 -05:00
7e4a37b07c Merge pull request #1168 from cnurse/dev
Create new UseOqtaneDatabase extension method
2021-03-09 09:46:14 -05:00
642bafb2fc Merge pull request #1170 from sbwalker/dev
Add folder structure to external module template client project to organize code and emphasize that a module project can contain multiple modules.
2021-03-09 09:46:05 -05:00
7f1f4ec4fb Rows -> Records 2021-03-08 20:57:06 +03:00
c64f350f36 Create new DbContextUtils class and move common SaveChanges code to this class. While MasterDbContext has no entities that support IDeletable it does not hurt to check and minimizes duplication 2021-03-06 16:06:29 -08:00
8376a09ad6 Create new UseOqtaneDatabase extension method and replace all uses of UseSqlServer.
This is a preparative step to locate all database configuration in one file.
2021-03-06 14:13:06 -08:00
13b45453c6 Fixed Issue with menu Icon Spacing. #1160 2021-03-05 15:31:38 +01:00
4c9960b983 Rows -> Maximum Rows 2021-03-05 16:41:53 +03:00
541bd5a279 Merge pull request #10 from oqtane/dev
Merge
2021-03-05 06:32:14 +01:00
745575c1f1 Fix UX issue where the Location field was not being updated when the user selected Create Module 2021-03-04 08:30:09 -05:00
af896e0fa8 Merge pull request #1155 from sbwalker/dev
Fix UX issue where the Location field was not being updated when the user selected Create Module
2021-03-04 08:29:54 -05:00
ab102f1883 Merge pull request #1154 from leigh-pointer/dev
Delete all Modules and Pages from Recycle bin
2021-03-04 08:29:45 -05:00
04caa8f725 Merge pull request #1151 from cnurse/dev
Add gitignore and README to Solution files and ad support for Jetbrains Rider
2021-03-04 08:28:23 -05:00
63fc1cd042 Update Index.razor 2021-03-03 17:15:00 +01:00
442a8018ce Merge branch 'dev' of https://github.com/leigh-pointer/oqtane.framework into dev 2021-03-03 17:07:04 +01:00
0fd46d28b5 Delete all modules and pages in recycle bin 2021-03-03 16:55:13 +01:00
a03434ac41 recycle bin and log paging 2021-03-03 16:39:32 +01:00
b08fdf8690 Merge branch 'dev' of https://github.com/leigh-pointer/oqtane.framework into dev 2021-03-03 13:26:01 +01:00
7205246718 Added Delete all pages and modules 2021-03-03 13:25:49 +01:00
a4f0afa0ab Merge branch 'master' into dev 2021-03-02 12:14:41 -08:00
dbc4e3ea66 Add the Jetbrains Rider "idea" folder to gitignore. 2021-03-02 12:07:04 -08:00
0d5ecb7427 Include gitignore and README files in Solution Files folder, so they are more accessible for editing 2021-03-02 12:03:26 -08:00
e1b04f5dae Merge pull request #9 from oqtane/dev
Merge pull request #1149 from leigh-pointer/dev
2021-03-02 16:52:28 +01:00
ea1bcba32f Merge pull request #1149 from leigh-pointer/dev
Fix for #1148 LangVersion set to 7.3
2021-03-02 10:48:13 -05:00
cccb0806e1 LangVersion Fix #1148
removed the LangVersion property from project
2021-03-02 16:17:33 +01:00
92a1804846 Merge pull request #8 from oqtane/dev
2.0.1
2021-02-28 08:29:21 +01:00
191a1dea4d Update README.md 2021-02-27 12:50:36 -05:00
be782ee001 Merge pull request #1143 from oqtane/master
sync with master
2021-02-27 12:34:28 -05:00
0d6413e8a5 Merge pull request #1142 from oqtane/dev
merge dev with master for 2.0.1 release
2021-02-27 12:32:37 -05:00
d69a392141 Merge pull request #7 from oqtane/dev
Commits on Feb 25,26,  2021
2021-02-26 15:49:06 +01:00
12fd845ed5 Fix issue when creating assets.json and folder does not exist. Improve module/theme uninstall to remove empty folders. 2021-02-26 09:08:25 -05:00
0eae1b690e Merge pull request #1139 from sbwalker/dev
Fix issue when creating assets.json and folder does not exist. Improve module/theme uninstall to remove empty folders.
2021-02-26 09:06:49 -05:00
5b9196cfd4 Update README.md 2021-02-26 08:05:36 -05:00
ba54076c61 Prepare for 2.0.1 release 2021-02-26 08:04:44 -05:00
8762809a83 Update README.md 2021-02-26 08:04:39 -05:00
c65bc1f4b1 Merge pull request #1138 from sbwalker/dev
Prepare for 2.0.1 release
2021-02-26 08:02:59 -05:00
99665800c4 remove SVG from allowable upload files 2021-02-26 07:48:16 -05:00
127a9c8deb Merge pull request #1137 from sbwalker/dev
remove SVG from allowable upload files
2021-02-26 07:46:35 -05:00
af1eebbf0d update copyright content 2021-02-26 07:45:10 -05:00
e5272d9725 Merge pull request #1136 from sbwalker/dev
update copyright content
2021-02-26 07:43:43 -05:00
4a2d3e452a Merge pull request #83 from oqtane/dev
sync
2021-02-25 14:27:51 -05:00
579992d8f2 Merge pull request #6 from oqtane/dev
Merge pull request #1135 from leigh-pointer/dev
2021-02-25 15:49:43 +01:00
dbecfc1c9f Merge pull request #1135 from leigh-pointer/dev
Fix for #1134 Files size incorrect when less than 1000 bytes
2021-02-25 08:31:29 -05:00
6c79006dd7 fix for #1134 Files size incorrect when less than 1000 bytes 2021-02-25 07:44:19 +01:00
53af3f2a14 Merge pull request #5 from oqtane/dev
Commits 19, 24 Feb
2021-02-25 06:54:59 +01:00
385ec178eb Merge pull request #1132 from oqtane/revert-1121-dev
Revert "Fix Upload SVG throw error #1120"
2021-02-24 17:27:20 -05:00
eda2a5637f Revert "Fix Upload SVG throw error #1120" 2021-02-24 17:26:54 -05:00
f60c60ac1d Merge pull request #1121 from leigh-pointer/dev
Fix Upload SVG throw error #1120
2021-02-24 17:22:23 -05:00
75a5262259 Merge pull request #1124 from PhilipMur/dev
Fixed site Favicon not saving
2021-02-24 17:22:02 -05:00
60d685416d Fixed site Favicon not saving
The saved function for the favicon was not there
2021-02-19 13:09:29 +00:00
b04cee22cd Merge branch 'dev' of https://github.com/leigh-pointer/oqtane.framework into dev 2021-02-18 09:27:57 +01:00
3af095997f Fixed when file size is less that 1 Kb displays 0. This can be misleading as many svg files are less that 1 kb. File size now displays to 2 deciaml places. 2021-02-18 09:27:51 +01:00
1aae8a1373 Update FileController.cs 2021-02-18 08:55:13 +01:00
bd762cb5c9 stream not needed 2021-02-18 08:52:57 +01:00
59ba4b2e05 doesnt need to open the stream if svg 2021-02-18 08:51:05 +01:00
45974a9c80 Update FileController.cs 2021-02-18 08:48:33 +01:00
9411f9a62e Merge branch 'dev' of https://github.com/leigh-pointer/oqtane.framework into dev 2021-02-18 08:39:18 +01:00
e3fc7c0ad1 #1120 Fix forUpload SVG throw error 2021-02-18 08:38:53 +01:00
a60859ba19 Merge pull request #4 from oqtane/dev
Login changes and log out path
2021-02-17 06:52:57 +01:00
eef26d02d3 Merge pull request #4 from oqtane/dev
Login changes
2021-02-17 06:46:35 +01:00
b7ce7bb3e4 Improve Login user experience when running on WebAssembly and address Logout scenario 2021-02-16 08:11:36 -05:00
e7b65aad19 Merge pull request #1114 from sbwalker/dev
Improve Login user experience when running on WebAssembly and address Logout scenario
2021-02-16 08:10:19 -05:00
cd89b2bf7b Merge pull request #82 from oqtane/dev
sync
2021-02-16 06:42:56 -05:00
4b3c488fb9 Merge pull request #3 from oqtane/dev
WebAssembly startup issue
2021-02-15 17:30:38 +01:00
9e8c574fb5 fix #1103 reported by @PhilipMur - incorrect life cycle method for Control Panel loading 2021-02-15 10:38:38 -05:00
dfa74f7eb6 Merge pull request #1111 from hishamco/lang-version
Remove LangVerion from Oqtane.Server
2021-02-15 10:37:58 -05:00
2b92dd51a1 Merge pull request #1112 from sbwalker/dev
fix #1103 reported by @PhilipMur - incorrect life cycle method for Control Panel loading
2021-02-15 10:37:27 -05:00
a47aa28c89 Merge pull request #2 from oqtane/dev
Section head and Languages
2021-02-15 05:49:29 +01:00
510f475816 Remove LangVerion from Oqtane.Server 2021-02-15 03:27:47 +03:00
de1107f046 Unauthenticated users need to be able to retrieve the list of languages 2021-02-14 18:35:42 -05:00
0f640dd68b Merge pull request #1110 from sbwalker/dev
Unauthenticated users need to be able to retrieve the list of languages
2021-02-14 18:34:33 -05:00
a50363fec7 Merge pull request #1107 from leigh-pointer/dev
#1106 Fix issue where Section would not initialize Expanded.
2021-02-14 15:03:09 -05:00
22c6ccffb8 Merge pull request #1 from oqtane/dev
Merge from base
2021-02-13 07:53:23 +01:00
fcd795be3d #1106 Fix issue where Section would not initialize collapsed. 2021-02-13 07:33:12 +01:00
6adda2b1a9 Merge pull request #1102 from leigh-pointer/dev
DirectorySeparator issue when black or forward slash.
2021-02-12 10:30:15 -05:00
0af8f427e1 Merge pull request #1101 from 2sic-forks/dev
fix typo memeber = member
2021-02-12 10:29:22 -05:00
c6e2c2e501 DirectorySeparator issue when black or forward slash.
Tested on Apple mac and Windows
2021-02-11 15:38:37 +01:00
278aab537f fix typo memeber = member 2021-02-11 10:41:43 +01:00
2fc877a99e fix #1097 - made PWA manifest Urls absolute rather than relative 2021-02-10 14:50:06 -05:00
22830a2cc1 Merge pull request #1098 from sbwalker/dev
fix #1097 - made PWA manifest Urls absolute rather than relative
2021-02-10 14:49:35 -05:00
478e4044b8 Fix #1094 - localization fallback logic 2021-02-08 08:17:13 -05:00
1fcb2ee2b3 Merge pull request #1096 from sbwalker/dev
Fix #1094 - localization fallback logic
2021-02-08 08:15:58 -05:00
05a98a9854 Merge pull request #81 from oqtane/dev
sync
2021-02-08 07:58:37 -05:00
00893459b2 Merge pull request #1095 from hishamco/localization-manager
Fix the localization manager resolution
2021-02-06 16:53:50 -05:00
7db1fe226d Fix the localization manager resolution 2021-02-06 22:40:11 +03:00
d0f3d388fe Merge pull request #1071 from hishamco/default-language-fix
Set default language if the culture not supported
2021-02-05 14:29:42 -05:00
13e356f510 Use LocalizationManager.GetDefaultCulture() 2021-02-05 21:47:50 +03:00
c3e7fa67f3 Performance improvement - set IsFixed="true" on ModuleState CascadingValues so that Blazor will not monitor them for changes 2021-02-05 09:37:10 -05:00
91be2ff4a0 Merge pull request #1090 from sbwalker/dev
Performance improvement - set IsFixed="true" on ModuleState CascadingValues so that Blazor will not monitor them for changes
2021-02-05 09:36:17 -05:00
3f29fd1b9f Merge remote-tracking branch 'upstream/dev' into default-language-fix 2021-02-05 15:35:52 +03:00
988639b603 module creator owner and module name cannot be the same 2021-02-04 09:36:19 -05:00
170289cc8e Merge pull request #1089 from sbwalker/dev
module creator owner and module name cannot be the same
2021-02-04 09:35:15 -05:00
077c40ee05 Merge pull request #1088 from sbwalker/dev
performance and user experience improvements
2021-02-04 08:55:51 -05:00
531cba715e performance and user experience improvements 2021-02-04 08:54:59 -05:00
1fb58296d8 Merge pull request #80 from oqtane/dev
sync
2021-02-01 14:40:42 -05:00
9ba2328a04 Merge pull request #1074 from Jayson-Furr/dev
Fixes to horizontal menu logic. Now supports two levels of menu items.
2021-01-28 09:02:49 -05:00
901d4ca5c4 Merge pull request #1070 from hishamco/default-language
Reset IsDefault per site Id for newly added language
2021-01-26 09:57:10 -05:00
033ee0497b Merge branch 'dev' of https://github.com/Jayson-Furr/oqtane.framework into dev 2021-01-23 21:16:27 -06:00
5a02ce6124 Fixes to vertical menu logic. Now supports multiple levels of menu items. Added FontIcon component to reduce duplicate code. 2021-01-23 21:15:54 -06:00
f60a4af6d2 Fixes to horizontal menu logic. Now supports multiple levels of menu items. Added FontIcon component to reduce duplicate code. 2021-01-23 21:14:44 -06:00
6fdbbeb8ce Fixes to horizontal menu logic. Now supports two levels of menu items. 2021-01-23 18:24:07 -06:00
90ca6aafe9 Set default language if the culture not supported 2021-01-24 01:06:50 +03:00
5a660f2634 Reset IsDefault per site Id for new language 2021-01-23 23:48:10 +03:00
7057f93f13 Merge pull request #1069 from sbwalker/dev
added HTML5 date picker to input controls
2021-01-22 14:21:41 -05:00
f637c9137c added HTML5 date picker to input controls 2021-01-22 14:19:43 -05:00
fbf2820571 Merge pull request #1067 from sbwalker/dev
Localization fixes - table definition, SQL script naming, SQL script not marked as Embedded Resource, changed column name from IsCurrrent to IsDefault to reflect intent, set default language for site in _Host
2021-01-22 13:01:37 -05:00
c0ed7c7934 Localization fixes - table definition, SQL script naming, SQL script not marked as Embedded Resource, changed column name from IsCurrrent to IsDefault to reflect intent, set default language for site in _Host 2021-01-21 17:09:34 -05:00
892d9c1ecf Merge pull request #1065 from sbwalker/dev
notification improvements
2021-01-18 14:41:52 -05:00
82a118b603 notification improvements 2021-01-18 14:39:56 -05:00
fc8f200c32 Merge pull request #79 from oqtane/dev
sync
2021-01-18 10:21:04 -05:00
b664bc2dbb remove Add Job component and make Type read-only in Edit 2021-01-18 10:19:42 -05:00
350d2cec96 Merge pull request #1061 from sbwalker/dev
set SiteState in HostedServiceBase for scheduled jobs
2021-01-18 10:18:47 -05:00
8be9fd6eb2 set SiteState in HostedServiceBase for scheduled jobs 2021-01-18 08:59:07 -05:00
b4aec286ed Merge pull request #1045 from hishamco/language-management
Add Languages Management
2021-01-18 08:30:31 -05:00
6a4fd26787 Merge pull request #1059 from sbwalker/dev
auto registration of scheduled jobs
2021-01-18 08:30:14 -05:00
a2029a3ca3 auto registration of scheduled jobs 2021-01-17 11:46:09 -05:00
bc0ba92303 Revert the changes in the LanguageController 2021-01-15 01:35:53 +03:00
e938d4f801 Add Admins role 2021-01-15 00:28:59 +03:00
54ff8eced1 Fix the relationship 2021-01-13 23:41:08 +03:00
a2943d083b Set culture when added language set to current 2021-01-13 18:43:26 +03:00
b3152ee3e5 LanguageSwitcher should have the cultures from language management 2021-01-13 18:26:36 +03:00
c5ae8c979b Cultures should come from supported cultures 2021-01-13 18:19:56 +03:00
4fd49ab028 Merge pull request #1046 from chlupac/ContentUrl
Add missing ContentUrl method
2021-01-12 09:41:38 -05:00
edbdfe454e Merge pull request #1047 from chlupac/GetPath
Introduce GetFolderPath and GetFilePath repository methods
2021-01-12 09:41:09 -05:00
f1a1a21d74 Introduce GetFolderPath and GetFilePath repository methods 2021-01-11 16:32:13 +01:00
1dcb14811d Add missing ContentUrl method 2021-01-11 16:31:37 +01:00
932c5590af Make sure one language is set to current 2021-01-11 00:11:30 +03:00
3a8fc428a6 Use TriaStateCheckBox for language IsCurrent 2021-01-11 00:04:43 +03:00
7d090e51a1 Add language page 2021-01-10 23:51:15 +03:00
128729d4a0 TenantId -> SiteId 2021-01-10 23:50:34 +03:00
70595eb90a Fix Language table 2021-01-10 23:50:21 +03:00
8ab511fda7 Return empty list if languages list are null 2021-01-10 23:17:35 +03:00
91a844c910 Add language management page template 2021-01-10 23:17:03 +03:00
21e09d95da Add migration script 2021-01-10 21:32:25 +03:00
9b4316d6cd Fix errors 2021-01-10 21:32:11 +03:00
3059e8c763 Add language service 2021-01-10 20:17:35 +03:00
aa19a35834 Add language repository & controller 2021-01-10 20:09:04 +03:00
1276c0269e add SMTP sender email 2021-01-07 15:06:48 -05:00
047fa9ac4e Merge pull request #1044 from sbwalker/dev
add SMTP sender email
2021-01-07 15:06:21 -05:00
b8fb230a0e Merge pull request #78 from oqtane/dev
sync with upstream
2021-01-05 17:12:40 -05:00
289c71d36f Merge pull request #1041 from sbwalker/dev
support for shared razor class library static resources in external module template
2021-01-05 17:11:52 -05:00
e3e5f782aa support for shared razor class library static resources in external module template 2021-01-05 17:11:45 -05:00
778f9cb356 added better validaton and user feedback related to SMTP configuration 2021-01-05 16:57:36 -05:00
767431fdab Merge pull request #1040 from sbwalker/dev
added better validaton and user feedback related to SMTP configuration
2021-01-05 16:57:03 -05:00
27f24160e4 Merge pull request #1039 from sbwalker/dev
fix navigation usability issue for shared add/edit page UI invoked by Control Panel and Page Management
2021-01-05 14:48:44 -05:00
1979a6dc4b Merge pull request #1021 from chlupac/UpdateSettings
Settings Fix
2021-01-05 14:47:46 -05:00
de25e3fbf1 fix navigation usability issue for shared add/edit page UI invoked by Control Panel and Page Management 2021-01-05 14:47:09 -05:00
91c5ff7b00 UpdateSettings bugfix
ISettingControl introduction
2021-01-05 19:52:14 +01:00
78f36c9f95 Merge pull request #1038 from sbwalker/dev
fixed issue with Sql Management and System Info missing icons after new installation
2021-01-05 09:00:55 -05:00
fb258805d0 Merge pull request #1037 from hishamco/resources
Fix ResourceKey property
2021-01-05 09:00:43 -05:00
a61a2f748c fixed issue with Sql Management and System Info missing icons after new installation 2021-01-05 09:00:42 -05:00
c82d6de40e Merge pull request #1033 from hishamco/list-entries-order
Order users by display name
2021-01-05 09:00:32 -05:00
c86a8cbd2d Fix ResourceKey property 2021-01-05 03:02:02 +03:00
b8622e5943 Order users by display name 2021-01-03 14:02:48 +03:00
98eff1f84e Merge pull request #77 from oqtane/dev
sync
2021-01-02 15:03:25 -05:00
a9a66155e2 Merge pull request #981 from hishamco/readme
Update README.md
2021-01-02 14:07:47 -05:00
9f829e3100 Merge pull request #1032 from sbwalker/dev
fix script order
2021-01-02 14:07:21 -05:00
ab2a317728 Merge pull request #1028 from hishamco/localization
Make external module localize ready
2021-01-02 14:07:05 -05:00
7d84f156f1 Merge pull request #1025 from hishamco/language-switcher
Show LanguageSwitcher if more than one culture
2021-01-02 14:06:34 -05:00
35d1adae99 fix script order 2021-01-02 13:26:36 -05:00
18b528c1af Merge pull request #76 from oqtane/dev
Merge pull request #1009 from sbwalker/dev
2021-01-02 13:22:51 -05:00
842442d27d Make external module localize ready 2021-01-01 00:19:58 +03:00
56e86edf9f Address feedback 2020-12-28 19:59:58 +03:00
f05b955c34 Show LanguageSwitcher if more than one culture 2020-12-28 18:15:28 +03:00
8ab01228ed Merge pull request #1009 from sbwalker/dev
allow developers to use custom icon libraries (Open Iconic is the default)
2020-12-22 08:37:43 -05:00
df382ce7a3 allow developers to use custom icon libraries (Open Iconic is the default) 2020-12-22 08:37:33 -05:00
05b0b74578 Merge pull request #75 from oqtane/dev
sync
2020-12-15 15:25:29 -05:00
3f8b9ef0c5 Merge pull request #997 from chlupac/content-disposition
FileController - content disposition
2020-12-15 09:02:12 -05:00
9d658e9166 Merge pull request #1001 from hishamco/revert-log-localization
Revert LogManager localization changes
2020-12-15 08:56:16 -05:00
07711c082e FileController - content disposition 2020-12-15 11:06:52 +01:00
eec680a151 Revert LogManager localization changes 2020-12-15 00:22:33 +03:00
deaaa74fc8 Update README.md 2020-12-14 08:52:01 -05:00
5dcfead4af Merge pull request #999 from sbwalker/dev
make QueryString parameter keys case insensitive - resolves #992
2020-12-12 15:59:19 -05:00
144c33bcab make QueryString parameter keys case insensitive - resolves #992 2020-12-12 15:58:58 -05:00
6c34c62e48 Merge pull request #74 from oqtane/dev
sync
2020-12-09 14:02:46 -05:00
01eab8fcfd Merge pull request #991 from hishamco/localize-alert-messages
Localize alert messages
2020-12-09 14:00:32 -05:00
e94069e8a5 Localize alert messages in UI 2020-12-09 21:17:33 +03:00
cf68d7320f Localize alert messages in controls 2020-12-09 21:15:35 +03:00
a29d7b524c Localize alert messages in admin pages 2020-12-09 21:12:00 +03:00
3c71282379 Merge pull request #980 from chlupac/file-controller
FileController fix
2020-12-09 11:48:38 -05:00
08f2877e80 Merge pull request #983 from hishamco/translations
Add missing localization strings
2020-12-09 09:54:36 -05:00
3c13122c37 Merge pull request #987 from hishamco/localizable-component
Localizable Component Enhancement
2020-12-09 09:54:15 -05:00
7ca69be41a Merge pull request #990 from hishamco/language-switcher
Replace Visible with ShowLanguageSwitcher property
2020-12-09 09:53:53 -05:00
7c70055b83 Merge pull request #988 from hishamco/ignore
Add Content folder to .gitignore
2020-12-09 09:52:44 -05:00
dd89296713 Replace Visible with ShowLanguageSwitcher property 2020-12-09 17:47:49 +03:00
941d08f0a2 Add missing Control Panel localization strings 2020-12-08 22:57:58 +03:00
3caf06d8ff Add Content folder to .gitignore 2020-12-08 22:50:36 +03:00
c35b5d861a Add missing User Profile localization strings 2020-12-08 22:46:28 +03:00
169a4b00a4 Add missing FileManager localization strings 2020-12-08 22:45:20 +03:00
62f6082525 Localize TriStateCheckBox control 2020-12-08 21:23:03 +03:00
c27e8b55df Add missing Users localization strings 2020-12-08 21:16:37 +03:00
2d15f5d185 Add missing Themes localization strings 2020-12-08 20:55:38 +03:00
836ba5eeb9 Add missing Tenanat localization strings 2020-12-08 20:55:28 +03:00
0e95e94cab Add missing Sql localization strings 2020-12-08 20:50:14 +03:00
8e4a7549ed Add missing Sites localization strings 2020-12-08 20:48:39 +03:00
29c37575f1 Add missing Roles localization strings 2020-12-08 20:45:54 +03:00
299e28abc4 Avoid Label.Text to be empty 2020-12-08 20:40:54 +03:00
5ce7284a6b Returns English version if the value is empty 2020-12-08 16:45:53 +03:00
df1d646083 Refactor LocalizableComponent 2020-12-08 16:37:55 +03:00
f123ac89ef Add missing RecylceBin localization strings 2020-12-08 15:08:21 +03:00
055ae26bc1 Add missing Logs localization strings 2020-12-08 14:41:03 +03:00
e0087c1dbb Add missing Jobs localization strings 2020-12-08 14:37:54 +03:00
87ba77fdba Localize AuditInfo 2020-12-08 13:43:05 +03:00
d953587e4b Merge pull request #986 from chlupac/returnurl
Fix - return to url during login fail when returnurl contains "/"
2020-12-07 17:06:04 -05:00
35e776b150 Fix - return to url during login fail when returnurl contains "/" 2020-12-07 21:27:00 +01:00
86beef305e Merge pull request #976 from jimspillane/FixAureDeploy
Fix Azure deploy
2020-12-07 14:06:25 -05:00
a526719663 Merge pull request #982 from hishamco/action-dialog
Localize ActionDialog.Text
2020-12-07 13:28:02 -05:00
f0f3055869 Add PermissionGrid localization strings 2020-12-06 21:46:19 +03:00
5e49206828 Add FileManager localization strings 2020-12-06 21:46:03 +03:00
5678a1796a Add missing Files localization strings 2020-12-06 21:45:33 +03:00
b878b3ee2f Localize ActionDialog.Text 2020-12-06 19:51:25 +03:00
8c9fc952d8 Update README.md 2020-12-06 19:28:12 +03:00
14f8155df6 FileController fix
- using PhysicalFile framework method (current implementation causes file locks and 500 error at heavy load)
- Add correct mimetype to header based on file extension
2020-12-06 16:23:28 +01:00
5273ae8c45 Merge pull request #975 from jimspillane/FixAureDeploy
Fix Azure deploy
2020-12-04 14:54:05 -05:00
1968b0283d Merge pull request #967 from hishamco/language-switcher
Add Language switcher
2020-12-04 14:53:48 -05:00
1b017a9651 Fix Azure Deploy
Add 5.0 to framework App Service siteConfig
2020-12-04 10:43:29 -05:00
2fe474268e Revert "Fix Azure Deploy"
This reverts commit d688a7c10da81d42fe92b8e1ca7ee266199b3705.
2020-12-04 10:34:52 -05:00
dc7cea1db0 Merge pull request #23 from oqtane/master
Sync Master
2020-12-04 10:20:02 -05:00
0ba348d73e Merge pull request #974 from sbwalker/dev
fix #957 related to loading resources within a Settings component
2020-12-03 15:19:44 -05:00
9f590b32f7 fix #957 related to loading resources within a Settings component 2020-12-03 15:19:23 -05:00
5e293ee298 Fix SupportedCultures bug 2020-12-03 17:25:01 +03:00
20f1a6175f Use cookie everywhere 2020-12-03 17:15:08 +03:00
049ded6f7e Fallback to default culture if its required 2020-12-03 16:14:23 +03:00
1a8125c26d Use REST style 2020-12-03 15:50:25 +03:00
5ee38e4ae7 Add Visible property to control the visibility 2020-12-03 14:13:01 +03:00
a37eb8a44a Introduce Culture model to avoid CultureInfo.DisplayName issue 2020-12-03 14:05:49 +03:00
1b3cc2c44e Interop local storage APIs should be generic 2020-12-03 13:37:18 +03:00
863c13d1d8 Merge pull request #968 from sbwalker/dev
Fixes to make site alias, page path, and module action Url resolutions case insensitive. Optimized logic for Settings component which fixed localization rendering issue.
2020-12-02 09:16:33 -05:00
fd60b40c53 Fixes to make site alias, page path, and module action Url resolutions case insensitive. Optimized logic for Settings component which fixed localization rendering issue. 2020-12-01 19:36:02 -05:00
fa3cc48fd0 Avoid to select the current selected culture 2020-12-02 02:19:47 +03:00
d4dd80ff32 Add missing event args 2020-12-02 02:15:52 +03:00
330499dda5 Use Interop 2020-12-02 02:10:01 +03:00
75556070d6 Ability to change culture 2020-12-02 02:04:34 +03:00
c4d1b16abb Add LanguageSwitcher component 2020-12-02 01:52:46 +03:00
c67e893b6e Add LocalizationService APIs 2020-12-02 01:38:32 +03:00
418c9888c4 Add LocalizationController 2020-12-02 01:38:00 +03:00
a875a5ad32 Merge pull request #73 from oqtane/dev
sync
2020-12-01 14:34:11 -05:00
e9a58138fc Update README.md 2020-11-30 18:59:44 -05:00
e57f0675c6 Merge pull request #954 from hishamco/localize-logs
Localize log messages
2020-11-30 08:14:35 -05:00
d964c25596 Add Resources folder 2020-11-27 22:06:54 +03:00
d5e48296ed Set RootNamespace 2020-11-27 22:06:40 +03:00
2ea0b7199b Localize log messages 2020-11-27 21:48:52 +03:00
a6b45014a4 Merge pull request #72 from oqtane/dev
sync
2020-11-25 16:53:03 -05:00
f6fafbfcfa Merge pull request #950 from hishamco/fix-action-link-localization
Fix localization issue in ActionLink
2020-11-25 16:52:24 -05:00
2f943546ba Merge pull request #951 from sbwalker/dev
Module Creator activation improvement
2020-11-25 16:52:08 -05:00
3fcbc07406 Module Creator activation improvement 2020-11-25 16:51:39 -05:00
7fd0c6c3aa Fix localization issue in ActionLink 2020-11-26 00:04:47 +03:00
23d273f3f7 Merge pull request #949 from sbwalker/dev
improve user experience of Module Creator flow
2020-11-25 11:59:45 -05:00
4401dba4ec improve user experience of Module Creator flow 2020-11-25 11:59:17 -05:00
4bf7dcc835 Merge pull request #947 from sbwalker/dev
improve user experience by delegating application restart responsibility to the host user
2020-11-24 16:23:25 -05:00
5e42ab8cca improve user experience by delegating application restart responsibility to the host user 2020-11-24 16:22:53 -05:00
1e1a2aa145 Update README.md 2020-11-24 10:48:30 -05:00
fa59ec1b24 Merge pull request #71 from oqtane/dev
sync
2020-11-22 10:41:36 -05:00
03d246340f Merge pull request #940 from sbwalker/dev
fix ModuleCreator Invalid character use in naming fields #889
2020-11-20 16:40:58 -05:00
19f924c3d3 fix ModuleCreator Invalid character use in naming fields #889 2020-11-20 16:40:25 -05:00
6185750aeb Merge pull request #938 from sbwalker/dev
missing IStringLocalizer<Index> causing compilation error
2020-11-20 10:30:32 -05:00
710aab9b93 missing IStringLocalizer<Index> causing compilation error 2020-11-20 10:30:07 -05:00
6d913c842c Merge pull request #70 from oqtane/dev
sync
2020-11-20 10:27:20 -05:00
8311d01f32 Merge pull request #935 from hishamco/localize-RTE
Localize RichTextEditor Component
2020-11-20 10:26:08 -05:00
9a3528a208 Merge pull request #934 from hishamco/localize-permission-grid
Localize PermissionGrid Component
2020-11-20 10:25:52 -05:00
acd77b79ba Merge pull request #932 from hishamco/localize-control-panel
Localize ControlPanel Component
2020-11-20 10:25:38 -05:00
50959199bd Merge pull request #931 from hishamco/localize-oqtane-theme
Localize Login & UserProfile components
2020-11-20 10:25:25 -05:00
5d06c356cd Merge pull request #930 from hishamco/localize-installer-page
Localizer Installer Page
2020-11-20 10:25:14 -05:00
066f6095e6 Merge pull request #929 from hishamco/localize-html-text
Localize HtmlText
2020-11-20 10:25:02 -05:00
2ead8fc850 Merge pull request #928 from hishamco/localize-modules-pages
Localize Modules Pages
2020-11-20 10:24:49 -05:00
6c3726be0c Merge pull request #927 from hishamco/localize-module-definitions-pages
Localize Module Definitions Pages
2020-11-20 10:24:36 -05:00
16b713ad6c Merge pull request #926 from hishamco/localize-module-creator-page
Localize Module Creator Page
2020-11-20 10:24:23 -05:00
7cad2e9bd6 Merge pull request #925 from hishamco/localize-reset-page
Localize Reset Page
2020-11-20 10:24:06 -05:00
93cd2e563a Merge pull request #924 from hishamco/localize-register-page
Localizer Register Page
2020-11-20 10:23:52 -05:00
b7e40373b4 Merge pull request #923 from hishamco/localize-files-pages
Localize Files Pages
2020-11-20 10:23:36 -05:00
0a7e2dba84 Merge pull request #922 from hishamco/localize-jobs-pages
Localize Jobs Pages
2020-11-20 10:23:21 -05:00
fe3801dcfb Merge pull request #921 from hishamco/localize-login-page
Localize Login Page
2020-11-20 10:23:05 -05:00
c205b9f930 Merge pull request #920 from hishamco/localize-logs-pages
Localize Logs Pages
2020-11-20 10:22:51 -05:00
7db52ce49a Merge pull request #919 from hishamco/localize-pages
Localize Pages
2020-11-20 10:22:36 -05:00
6cf6e312a8 Merge pull request #918 from hishamco/localize-profiles-pages
Localize Profiles Pages
2020-11-20 10:22:21 -05:00
88ba43557c Merge pull request #917 from hishamco/localize-recycle-bin-page
Localize Recycle Bin
2020-11-20 10:22:03 -05:00
13e98e9d51 Merge pull request #937 from sbwalker/dev
missing using statement causing compilation error
2020-11-20 10:21:00 -05:00
d64f06eb71 missing using statement causing compilation error 2020-11-20 10:20:29 -05:00
26f610e43b Localize RichTextEditor component 2020-11-20 02:25:45 +03:00
33c8fe6e33 Localize PermissionGrid component 2020-11-20 02:22:21 +03:00
5cee11accb Localize ControlPanel component 2020-11-20 02:11:16 +03:00
f8e1c8c53d Localize Login & UserProfile components 2020-11-20 02:05:48 +03:00
f9a0ecca61 Localizer installer page 2020-11-20 01:56:25 +03:00
c7cb3ad53a Localize HtmlText 2020-11-20 01:53:24 +03:00
d108cc3990 Localize non components for the modules pages 2020-11-20 01:45:09 +03:00
867c8f84ad Localize components for the modules pages 2020-11-20 01:43:02 +03:00
4599e9a0fc Localize non components for the module definitions pages 2020-11-20 01:39:56 +03:00
a77a86a439 Localize components for the module definitions pages 2020-11-20 01:37:18 +03:00
46bf682819 Localize module creator page 2020-11-20 01:32:15 +03:00
d45035ce56 Localize reset page 2020-11-20 01:27:30 +03:00
d5d13c6def Localizer register page 2020-11-20 01:26:01 +03:00
6b61713205 Localize non components for the files pages 2020-11-20 01:20:59 +03:00
fb3bd51227 Localize components for the files pages 2020-11-20 01:17:36 +03:00
177dcad5a1 Localize non components for the jobs pages 2020-11-20 01:12:15 +03:00
bc570de9e6 Localize components for jobs pages 2020-11-20 01:06:57 +03:00
be1237f748 Localize login page 2020-11-20 01:00:50 +03:00
db6e550c86 Localize non components for the logs pages 2020-11-20 00:56:37 +03:00
e1b02ee405 Localize components for the logs pages 2020-11-20 00:53:55 +03:00
9a3fd94c11 Localize non components for the pages 2020-11-20 00:50:14 +03:00
917caab7a1 Localize components of the pages 2020-11-20 00:44:44 +03:00
fcfcd46d8e Localize non components for the profiles pages 2020-11-20 00:37:10 +03:00
387c1aa57f Localize component for the profiles pages 2020-11-20 00:34:39 +03:00
09d84bc12a Localize Recylce Bin 2020-11-20 00:30:07 +03:00
2bf5e6c6b3 Merge pull request #69 from oqtane/dev
sync
2020-11-19 15:54:32 -05:00
301051898b Merge pull request #916 from hishamco/runtime
Runtime enum should be in Oqtane.Shared
2020-11-19 15:37:19 -05:00
217bb972e1 Merge pull request #901 from hishamco/localize-site-setiings-page
Localize Site Settings Page
2020-11-19 15:34:38 -05:00
3b62ffd4f0 Merge pull request #900 from hishamco/localize-sites-pages
Localize Sites Pages
2020-11-19 15:34:25 -05:00
ab5bf3cda5 Merge pull request #899 from hishamco/localize-sql-page
Localize SQL Page
2020-11-19 15:34:12 -05:00
db50a2de5f Merge pull request #898 from hishamco/localize-system-info-page
Localize system info page
2020-11-19 15:34:01 -05:00
4a8dd2dc6a Merge pull request #897 from hishamco/localize-tenants-pages
Localize Tenants Pages
2020-11-19 15:33:48 -05:00
6c445b1202 Merge pull request #896 from hishamco/localize-themes-pages
Localize Themes Pages
2020-11-19 15:33:36 -05:00
4973dbb9a5 Merge pull request #895 from hishamco/localize-upgrade-page
Localize Upgrade Page
2020-11-19 15:33:21 -05:00
bd198fef7b Merge pull request #894 from hishamco/localize-user-profiles-pages
Localize User Profile Pages
2020-11-19 15:33:08 -05:00
e25146cd68 Merge pull request #893 from hishamco/localize-users-pages
Localize Users Pages
2020-11-19 15:32:55 -05:00
a849ee283e Merge pull request #892 from hishamco/localize-roles-pages
Localize Roles Pages
2020-11-19 15:32:41 -05:00
57357de2a7 Merge pull request #891 from hishamco/default-localized-string
Fallback to English as default culture
2020-11-19 15:32:25 -05:00
34fb41fac7 Merge pull request #67 from oqtane/dev
sync
2020-11-19 15:02:16 -05:00
66b2718fbb Merge pull request #914 from sbwalker/master
Fix #904 - module title not updating in UI after modification
2020-11-19 14:29:26 -05:00
48aa051a11 Merge pull request #913 from sbwalker/master
Fix #904 - module title not updating in UI after modification
2020-11-19 14:24:32 -05:00
3967c7c783 Fix #904 - module title not updating in UI after modification 2020-11-19 14:22:30 -05:00
80dd3cae92 Update README.md 2020-11-19 14:20:44 -05:00
322d45dd12 Runtime enum should be in Oqtane,Shared 2020-11-19 17:33:49 +03:00
a38cb25804 Merge pull request #21 from oqtane/master
sync master
2020-11-18 22:36:01 -05:00
1f91d7d1d2 Merge pull request #66 from oqtane/master
sync
2020-11-17 17:15:15 -05:00
e94741d049 Localize site settings page 2020-11-18 00:17:16 +03:00
5155cb214f Localize non component of the sites pages 2020-11-18 00:13:46 +03:00
236a4bd3d9 Localize components of sites page 2020-11-18 00:09:51 +03:00
1518afbd93 Localize SQL page 2020-11-17 23:57:58 +03:00
a14616a7c1 Localize system info page 2020-11-17 23:54:59 +03:00
2672f47cdf Localize non components of the tenants pages 2020-11-17 23:47:38 +03:00
bbda300952 Localize components for the tenants pages 2020-11-17 23:46:14 +03:00
c1fa6589af Localize non components for themes pages 2020-11-17 23:42:27 +03:00
1b44de6972 Localize components for the themes pages 2020-11-17 23:40:23 +03:00
2d9ad076d2 Localize upgrade page 2020-11-17 23:34:49 +03:00
dca607b22f Localize non components for user profile pages 2020-11-17 23:24:14 +03:00
5c3e22ab34 Localize components of the user profile pages 2020-11-17 23:13:13 +03:00
086e3623c3 Localize non components in users pages 2020-11-17 23:04:16 +03:00
261f48e842 Localize components for the users pages 2020-11-17 22:56:41 +03:00
b4333a743d Localize non components strings in roles pages 2020-11-17 22:44:44 +03:00
c70f37d33c Fix resource keys 2020-11-17 22:43:53 +03:00
16b5dd99cc Localize components for the roles pages 2020-11-17 22:39:38 +03:00
d8d1412a8f Fallback to English as default culture 2020-11-17 22:28:17 +03:00
c44bc8709d Update README.md 2020-11-16 08:30:02 -05:00
d9f9e73480 Update README.md 2020-11-13 10:00:31 -05:00
2b371b2a9f Update README.md 2020-11-11 11:35:57 -05:00
232cf77e84 Update README.md 2020-11-11 11:35:21 -05:00
080c9ff38b Update README.md 2020-11-11 11:33:38 -05:00
9250a03aea Update README.md 2020-11-11 11:32:56 -05:00
07ae0edd30 Merge pull request #879 from sbwalker/master
removed unnecessary file
2020-11-11 10:24:39 -05:00
32e8052079 removed unnecessary file 2020-11-11 10:24:10 -05:00
60ccad3106 Merge pull request #878 from sbwalker/master
fixed regression issue which was preventing proper handling of situations where module assembly is missing
2020-11-11 10:19:40 -05:00
bcc00a2dbb fixed regression issue which was preventing proper handling of situations where module assembly is missing 2020-11-11 10:19:04 -05:00
5080040590 Merge pull request #65 from oqtane/master
sync
2020-11-11 09:02:02 -05:00
0d71a3878f Merge pull request #877 from jimspillane/UpgradeExternalModuleTemplateToNET50
Upgrade external module template to net50
2020-11-11 09:00:36 -05:00
82b16d28ff Upgrade External Module Template to NET5.0 2020-11-10 21:02:51 -05:00
e34ffb716d Merge pull request #20 from oqtane/master
sync master
2020-11-10 20:52:37 -05:00
6f8c2fb2ed Merge pull request #875 from hishamco/net5
Update README.md
2020-11-10 16:29:21 -05:00
3970722abf Merge pull request #876 from sbwalker/master
include syncevents on module add/update/delete to trigger reload in UI
2020-11-10 16:27:37 -05:00
880ad0486a include syncevents on module add/update/delete to trigger reload in UI 2020-11-10 16:26:50 -05:00
a02786b8b0 Update README.md 2020-11-11 00:22:07 +03:00
2bea59fc66 Merge pull request #874 from hishamco/net5
Upgrade packages to .NET 5 RTM
2020-11-10 16:15:20 -05:00
45819aae07 Upgrade packages to .NET 5 RTM 2020-11-10 23:47:43 +03:00
4762fac0ce Merge pull request #873 from sbwalker/master
improve user experence of Module Creator during app restart
2020-11-09 15:53:14 -05:00
8b97872100 improve user experence of Module Creator during app restart 2020-11-09 15:52:40 -05:00
b921ec24ab Merge pull request #872 from sbwalker/master
fixed issue where modulestate was being modified and not treated as a readonly cache
2020-11-09 15:36:36 -05:00
07519bccde fixed issue where modulestate was being modified and not treated as a readonly cache 2020-11-09 15:35:32 -05:00
a8932ec1e0 Merge pull request #64 from oqtane/master
sync
2020-11-09 10:51:23 -05:00
a9bb82347d Merge pull request #871 from sbwalker/master
Removed comment and added defensive logic in the eent that the moduletype is not valid. Also changed default behavior to display the name if the key is missing. Will need an option in the future to enable the display of missing keys.
2020-11-09 09:43:52 -05:00
2c58a97ec1 fixed malformed tokens in external module template 2020-11-09 09:42:01 -05:00
41b30bfab2 Removed comment and added defensive logic in the eent that the moduletype is not valid. Also changed default behavior to display the name if the key is missing. Will need an option in the future to enable the display of missing keys. 2020-11-09 09:27:52 -05:00
a77aa5fe18 Merge pull request #63 from oqtane/master
sync
2020-11-09 08:29:14 -05:00
780b0c704c Merge pull request #870 from hishamco/localization
Use IStringLocalizerFactory for simplicity
2020-11-08 14:58:42 -05:00
26c054c22d Use IStringLocalizerFactory for simplicity 2020-11-08 22:26:04 +03:00
047336ad21 Merge pull request #62 from oqtane/master
sync
2020-11-08 10:52:36 -05:00
2d344975ff Merge pull request #869 from sbwalker/master
Module Creator should only include Framework References for 2.0.0 and above
2020-11-07 16:45:30 -05:00
e0017065af Module Creator should only include Framework References for 2.0.0 and above 2020-11-07 16:44:41 -05:00
f0a9739b37 Merge pull request #868 from sbwalker/master
upgrade Module Creator external template to .NET 5 RC2 for testing. In order to use it you must choose Local as the Target Version in the Module Creator.
2020-11-07 16:36:48 -05:00
5e801bd5ee upgrade Module Creator external template to .NET 5 RC2 for testing. In order to use it you must choose Local as the Target Version in the Module Creator. 2020-11-07 16:35:44 -05:00
824df0e849 Update README.md 2020-11-05 10:27:20 -05:00
a24fd099c0 Update README.md 2020-11-05 09:25:46 -05:00
c6fdc99690 Merge pull request #866 from sbwalker/master
move version to 2.0.0 and target to net5.0
2020-11-05 09:20:59 -05:00
b602113cd1 move version to 2.0.0 2020-11-05 09:19:56 -05:00
b6886116ad Merge pull request #865 from sbwalker/master
fixed JavaScript issue when loading scripts dynamically which caused only the first script to be loaded for a module
2020-11-05 08:51:58 -05:00
f976910730 fixed JavaScript issue when loading scripts dynamically which caused only the first script to be loaded 2020-11-05 08:50:52 -05:00
6726a42406 Merge pull request #864 from sbwalker/master
resolve Login UI issue #794
2020-11-04 17:44:09 -05:00
23a35cf3c4 resolve Login UI issue #794 2020-11-04 17:43:27 -05:00
f5b99bf9d1 Merge pull request #863 from sbwalker/master
implement optional Security parameter for TabPanel #797
2020-11-04 17:27:58 -05:00
d7135ad4f9 implement optional Security parameter for TabPanel #797 2020-11-04 17:27:15 -05:00
0d03f59cd4 Merge pull request #862 from sbwalker/master
use logo with glow effect so it is visible on both black and white backgrounds
2020-11-04 16:59:03 -05:00
5b49e1bc7c use logo with glow effect so it is visible on both black and white backgrounds 2020-11-04 16:58:02 -05:00
a3215e286a Merge pull request #61 from oqtane/master
sync
2020-11-04 16:00:53 -05:00
b10140f513 Merge pull request #861 from sbwalker/master
performance optimization to reduce calls from client to server unless content is changed
2020-11-04 15:59:49 -05:00
8eaa298d8b performance optimization to reduce calls from client to server unless content is changed 2020-11-04 15:58:13 -05:00
c52d255a30 Merge pull request #860 from sbwalker/master
load module settings automatically so that they are part of the ModuleState and can be easily accessed by developers
2020-11-04 15:41:48 -05:00
97cec46ec1 load module settings automatically so that they are part of the ModuleState and can be easily accessed by developers 2020-11-04 15:40:57 -05:00
46c1564e3d Merge pull request #859 from sbwalker/master
optimize performance when running on WebAssembly by caching assembly payload that needs to be served to new clients
2020-11-04 08:11:03 -05:00
700b6e2d68 optimize performance when running on WebAssembly by caching assembly payload that needs to be served to new clients 2020-11-04 08:10:21 -05:00
3cfb27b2bc Merge pull request #857 from sbwalker/master
fixed compatibility issue in .NET5/WebAssembly where assemblies were not being loaded into the default AppDomain, optimized service registration on WebAssembly, fixed spelling mistake for satellite assemblies constant and fixed issue in LocalizableComponent
2020-11-03 14:44:23 -05:00
b4b73b7e5a fixed compatibility issue in .NET5/WebAssembly where assemblies were not being loaded into the default AppDomain, optimized service registration on WebAssembly, fixed spelling mistake for satellite assemblies constant and fixed issue in LocalizableComponent 2020-11-03 14:41:49 -05:00
8c2338e590 Update README.md 2020-10-28 07:54:35 -04:00
89875516e6 Update README.md 2020-10-25 21:07:56 -04:00
15aa2c2e47 Merge pull request #60 from oqtane/master
sync
2020-10-25 13:11:01 -04:00
1bf4bd0022 Merge pull request #850 from hishamco/patch-1
Update README.md
2020-10-25 13:09:23 -04:00
570b885c09 Merge pull request #849 from hishamco/section-localization
Localize section component
2020-10-25 13:08:40 -04:00
90de3949d2 Merge pull request #848 from hishamco/tab-panel-localization
Localize TabPanel component
2020-10-25 13:08:20 -04:00
38eb7c05b3 Update README.md 2020-10-24 10:12:48 +03:00
5948e7ba76 Localize section component 2020-10-24 10:05:57 +03:00
3cbf55e1ce Move DisplayHeading() from TabStrip to TabPanel 2020-10-24 09:52:17 +03:00
ec270fbff0 Localize TabPanel component 2020-10-24 09:44:56 +03:00
03926bccb0 Merge pull request #846 from sbwalker/master
fix folder parsing issue specific to Azure environment where WebRootPath contains 2 wwwroot nested folders
2020-10-22 16:31:41 -04:00
cdb7de84fa fix folder parsing issue specific to Azure environment where WebRootPath contains 2 wwwroot nested folders 2020-10-22 16:30:27 -04:00
76eccececa Update README.md 2020-10-21 09:20:47 -04:00
2f17945020 Merge pull request #843 from sbwalker/master
Fixed issue where Page Url expansion script for 1.0.4 was not implemented properly - it was not tagged as an embedded resource.
2020-10-21 08:02:57 -04:00
cbd7caa6df Merge branch 'master' of https://github.com/sbwalker/oqtane.framework into master 2020-10-21 08:01:39 -04:00
0ef04e81ff Fixed issue where Page Url expansion script for 1.0.4 was not implemented properly - it was not tagged as an embedded resource. 2020-10-21 08:01:17 -04:00
4dbe16bee5 Merge pull request #841 from hishamco/action-link-localization
Localize ActionDialog
2020-10-20 16:47:34 -04:00
3f78c99ed4 Localize ActionDialog 2020-10-20 23:16:53 +03:00
eaa8649699 Merge pull request #59 from oqtane/master
sync
2020-10-20 12:54:28 -04:00
5835e037a7 Merge pull request #839 from hishamco/action-link-localization
Fix issue with ActionLink localization
2020-10-20 12:50:42 -04:00
af41e8bcfb Fix issue with ActionLink localization 2020-10-20 18:36:06 +03:00
d19f3f358b Update README.md 2020-10-20 09:13:43 -04:00
fc7000394f Merge pull request #58 from oqtane/master
sync
2020-10-20 09:05:32 -04:00
892b79833b Merge pull request #837 from mikecasas/master
Deleted some white space.
2020-10-20 07:53:58 -04:00
ef51d5f05d Merge pull request #836 from TonyValenti/master
Factor out Policy Names
2020-10-20 07:53:19 -04:00
40d8315cff Merge pull request #834 from hishamco/lang
Remove LangVersion
2020-10-20 07:52:55 -04:00
544475c489 Merge pull request #828 from hishamco/action-link-localization
Action link localization
2020-10-20 07:52:40 -04:00
e61cd3d366 Merge pull request #824 from hishamco/localizable-component
Localizable component
2020-10-20 07:52:11 -04:00
01fc60d35d Merge remote-tracking branch 'upstream/master' into master 2020-10-20 07:32:33 -04:00
17a9710c14 Delete white space. 2020-10-20 07:31:24 -04:00
8cf846ba90 Factor out Policy Names
Change AppDomain to AppContext
2020-10-19 20:04:13 -05:00
70a345d2a9 Remove LangVersion 2020-10-19 22:11:51 +03:00
79b584f268 Check needed if the component is localizable 2020-10-19 21:47:59 +03:00
3b09699618 Merge pull request #831 from mikecasas/master
Update template to use the new constants.
2020-10-19 09:48:32 -04:00
1315e0382e Update template to use the new constants. 2020-10-19 09:26:28 -04:00
c733707adc Merge pull request #830 from sbwalker/master
modify comment
2020-10-19 08:51:21 -04:00
9294537e23 modify comment 2020-10-19 08:50:37 -04:00
2abc2cdf20 Merge pull request #829 from sbwalker/master
introduce Resource Declaration and Location properties to offer more resource management options for developers
2020-10-19 08:04:00 -04:00
ecacb681b4 introduce Resource Declaration and Location properties to offer more resource management options for developers 2020-10-19 08:03:04 -04:00
34b9903b15 Localize ActionLink 2020-10-19 12:16:35 +03:00
fed56098a0 Avoid resource check in child components 2020-10-19 11:36:05 +03:00
fd5d777d3a Label should use LocalizableComponent 2020-10-19 11:17:34 +03:00
5bb7c63d44 Introduce LocalizableComponent 2020-10-19 11:16:46 +03:00
34122a344d Merge pull request #818 from sbwalker/master
upgrade to .NET5 RC2
2020-10-18 09:31:16 -04:00
74026401a6 upgrade to .NET5 RC2 2020-10-18 09:30:24 -04:00
f5beb54ddb Merge pull request #817 from sbwalker/master
fixed compilation warnings in AuditInfo, fixed issue in ModuleMessage triggered in InstallWizard, fixed PWA JavaScript in ThemeBuilder for all browsers
2020-10-18 09:10:06 -04:00
d082c5427b fixed compilation warnings in AuditInfo, fixed issue in ModuleMessage triggered in InstallWizard, fixed PWA JavaScript in ThemeBuilder for all browsers 2020-10-18 09:09:18 -04:00
f3e2177423 Merge pull request #57 from oqtane/master
sync
2020-10-18 08:54:34 -04:00
7c8beac3dc Merge pull request #788 from sbwalker/master
Changes for .NET 5
2020-10-18 08:53:11 -04:00
1293b98226 Merge pull request #801 from hishamco/localizable-labels
Use ServiceActivator instead of IHttpContextAccessor
2020-10-18 08:52:13 -04:00
63f43bc27e Merge pull request #802 from iJungleboy/master
Add Documentation attributes #570
2020-10-18 08:52:01 -04:00
f9296ec5c5 Merge pull request #816 from hishamco/editor-config
Add .editorconfig
2020-10-18 08:51:48 -04:00
9543cd7031 Merge pull request #805 from TonyValenti/master
Factored out Contants.*** Role into RoleNames.***
2020-10-18 08:51:23 -04:00
857d699c0d Add .editorconfig 2020-10-18 00:55:16 +03:00
c683de2cda Refactor TenantNames.Master 2020-10-16 10:45:13 -05:00
3e71bdfef3 Replace string with System.Net.Mime.MediaTypeNames.Application.Octet 2020-10-16 10:38:19 -05:00
766be6c929 Factor out default controller route. 2020-10-16 10:37:17 -05:00
f33fb4d001 Factoring out Constants.AdminPane and Constants.HostUser 2020-10-16 10:23:17 -05:00
becc779db8 Extracted "ViewModule" and "EditModule" into PolicyNames class. 2020-10-16 10:07:01 -05:00
955e7a3856 Factored out Contants.*** Role into RoleNames.***
Renamed 'AllUsers' to 'Everyone'
2020-10-16 06:22:52 -05:00
06c041dd4e Add Documentation attributes
part of https://github.com/oqtane/oqtane.framework/issues/570
2020-10-15 10:38:25 +02:00
4a90e6e64f Use ServiceActivator instead of IHttpContextAccessor 2020-10-15 06:07:11 +03:00
81475fd835 Merge pull request #796 from hishamco/localizable-labels
Support label localization
2020-10-13 07:52:24 -04:00
edc65e66c9 Use AddHttpContextAccessor() 2020-10-12 18:26:04 +03:00
4b11bdc4be Support label localization 2020-10-12 18:15:08 +03:00
67067a884b Merge pull request #791 from hishamco/localization-configuration
Simplify localization settings configurations
2020-10-11 20:34:56 -04:00
86bb6d1ea8 Simplify localization settings configurations 2020-10-10 22:19:21 +03:00
7b1a2fb887 Merge pull request #789 from hishamco/debug
Add blazor error details on DEV environment
2020-10-09 14:28:34 -04:00
b3db92ee95 Add blazor error details on DEV environment 2020-10-09 19:17:42 +03:00
aad10ab1c4 Changes for .NET 5 2020-10-08 11:20:43 -04:00
3ab9510e2a Update README.md 2020-10-08 09:59:05 -04:00
fde43b6c39 Merge pull request #783 from mikecasas/master
Refactor to eliminate repetitive code.
2020-10-07 09:24:43 -04:00
7b3dfc49b2 Refactor to eliminate repetitive code. 2020-10-06 08:11:00 -04:00
0a9edd8916 Merge pull request #781 from sbwalker/master
Fixed build warnings related to ModuleMessage component changes
2020-10-05 09:12:52 -04:00
0c0916c6ab Fixed build warnings related to ModuleMessage component changes 2020-10-05 09:11:47 -04:00
ece8f3a57e Merge pull request #56 from oqtane/master
sync
2020-10-04 10:57:24 -04:00
3d7630d3d4 Update README.md 2020-10-04 10:43:09 -04:00
8b6d0d3c7f Merge pull request #779 from hishamco/supported-cultures
Skip missed satellite assemblies forlders
2020-10-04 10:29:44 -04:00
ce37d2f2d2 Skip missed satellite assemblies forlders 2020-10-03 23:26:44 +03:00
40524300bf Merge pull request #777 from hishamco/remove-warning
Avoid Building ServiceProvider in ConfigureServices
2020-10-03 15:52:04 -04:00
5ae9daf5f2 Merge pull request #778 from sbwalker/master
added DefaultAction property to IModule (#765)
2020-10-03 15:51:30 -04:00
6a7be12758 added DefaultAction property to IModule (#765) 2020-10-03 15:50:15 -04:00
bcb6c81e43 Avoid Building ServiceProvider in ConfigureServices 2020-10-03 22:41:48 +03:00
26c40fb367 Merge pull request #55 from oqtane/master
sync
2020-10-03 15:15:16 -04:00
e934a28c39 Merge pull request #770 from PoisnFang/flexible-index-page-in-custom-modules
Allows page to find Custom Index page in Module from Actions Property
2020-10-03 15:14:18 -04:00
c2ca55627e comment where index page is specifed if no action 2020-10-03 12:12:23 -07:00
4a6ffacf56 Merge pull request #54 from oqtane/master
sync
2020-10-03 14:27:02 -04:00
bc72e28d11 Merge pull request #767 from PoisnFang/fix-pane-div-default-class
add default class 'container' for div in pane to avoid content squishing
2020-10-03 14:19:16 -04:00
2a402497cf only add div on admin border 2020-10-01 16:02:14 -07:00
49985dcf9e Merge pull request #53 from oqtane/master
sync
2020-10-01 10:08:22 -04:00
666721bf1a Merge pull request #762 from hishamco/localization-support
Localization support
2020-10-01 10:06:01 -04:00
5f56bc288b Merge pull request #750 from hishamco/alert-component
Use ModuleMessage component everywhere
2020-10-01 10:05:43 -04:00
6e41cd850e allows page to find Custom Index page in Module from Actions Property 2020-09-30 22:53:41 -07:00
f70fed66ae add default class 'container' for div in pane to avoid content squishing 2020-09-30 16:22:46 -07:00
2e2d46996a Refactoring 2020-09-30 00:07:00 +03:00
f83c1b1741 Use invariant culture by default 2020-09-29 22:12:03 +03:00
2924e7849f Read supported cultures from appsettings.json 2020-09-29 21:23:22 +03:00
437170671f Support server-sider localization 2020-09-29 20:20:38 +03:00
b52dd571ee Fix loading bug 2020-09-29 19:18:56 +03:00
ad9146ead1 Fix stallite assemblies folder path 2020-09-29 19:14:48 +03:00
52d1d5841e Avoid looking for en-US culture resources 2020-09-29 18:50:06 +03:00
468327d597 Set localization RootNamespace to make it works 2020-09-29 18:29:18 +03:00
7f28c5f2ff Add localization service to Oqtane.Client 2020-09-29 18:28:30 +03:00
accf947afd LoadClientAssemblies adds satellite assemblies 2020-09-29 18:28:02 +03:00
ec73c958c9 AddOqtaneParts -> AddOqtane 2020-09-29 18:03:24 +03:00
396d584615 Dowanlod satellite assemblies to the browser 2020-09-29 18:01:57 +03:00
edecfa10cd Load satellite assemblies on startup 2020-09-29 17:31:54 +03:00
0796ce54a9 Add localization settings 2020-09-29 17:30:56 +03:00
0044f031aa Set component params instead of SetModuleMessage 2020-09-23 11:29:05 +03:00
913ad53302 Use ModuleMessage everywhere 2020-09-20 15:43:01 +03:00
ad5f5fbc24 Replace Alert with ModuleMessage component 2020-09-20 15:10:48 +03:00
14746f47da Merge pull request #746 from mikecasas/patch-2
Update Constants.cs
2020-09-17 18:53:15 -04:00
0d76070663 Use alert component in FileManager 2020-09-17 15:42:03 +03:00
c01cd3b46c Add dismissible option 2020-09-17 15:41:34 +03:00
3613ce62eb Add Alert component 2020-09-17 15:09:20 +03:00
61839d8e46 Merge pull request #748 from sbwalker/master
fixed Theme install/uninstall issue, fixed Layout inheritance issue, fixed File server performance issue, cleaned up remaining hardcoded permission strings
2020-09-16 15:25:33 -04:00
8196112a59 fixed Theme install/uninstall issue, fixed Layout inheritance issue, fixed File server performance issue, cleaned up remaining hardcoded permission strings 2020-09-16 15:24:07 -04:00
465cbe3c96 Update Constants.cs
Hopefully, nothing wrong with uploading a csv file.
2020-09-16 14:18:22 -04:00
38f2fa5733 Merge pull request #735 from sbwalker/master
prepare for 1.0.4 release
2020-09-09 12:02:11 -04:00
7f15a5f464 prepare for 1.0.4 release 2020-09-09 12:01:16 -04:00
0c5d992d18 Merge pull request #52 from oqtane/master
sync
2020-09-09 11:49:45 -04:00
57dd983c1f Update README.md 2020-09-03 15:59:29 -04:00
d89927ca96 Update README.md 2020-09-02 15:20:17 -04:00
63744d9ec2 Update README.md 2020-09-02 15:17:56 -04:00
c67526b5b0 Update README.md 2020-09-01 16:44:33 -04:00
1cb6bf2a6b Merge pull request #724 from sbwalker/master
removed background color
2020-09-01 16:28:16 -04:00
ac9969c1b6 removed background color 2020-09-01 16:27:42 -04:00
510cd23d5e Update README.md 2020-09-01 16:24:27 -04:00
c94ccbff69 Merge pull request #723 from sbwalker/master
created architecture diagram
2020-09-01 16:23:21 -04:00
93d0cc5e1a created architecture diagram 2020-09-01 16:22:40 -04:00
075ea0aafd Merge pull request #51 from oqtane/master
sync
2020-08-31 10:04:27 -04:00
e75fe19103 Merge pull request #720 from sbwalker/master
add support for SVG and ICO files
2020-08-31 10:01:15 -04:00
e76f1b9663 use Label component in Module Creator templates 2020-08-31 10:00:30 -04:00
cb1c725ec1 add support for SVG and ICO files 2020-08-31 09:48:51 -04:00
98cd361fc0 Merge pull request #716 from sbwalker/master
enhanced Module Creator to allow developer to specify framework reference version so that modules can target any version including the local development environment
2020-08-29 11:30:53 -04:00
d0c8399dd9 enhanced Module Creator to allow developer to specify framework reference version so that modules can target any version including the local development environment 2020-08-29 11:30:16 -04:00
4effa8ec66 Merge pull request #715 from sbwalker/master
improved module/theme installation by saving the list of files which are in the Nuget package and using that list to remove them during uninstall
2020-08-29 10:56:26 -04:00
4065d87a74 improved module/theme installation by saving the list of files which are in the Nuget package and using that list to remove them during uninstall 2020-08-29 10:55:40 -04:00
eb9acc770c Merge pull request #714 from sbwalker/master
added support for dynamic inclusion of global resources in _host.cshtml ( ie. global stylesheets and scripts such as those required by UI component suites )
2020-08-28 11:25:38 -04:00
a8cd84e798 added support for dynamic inclusion of global resources in _host.cshtml ( ie. global stylesheets and scripts such as those required by UI component suites ) 2020-08-28 11:24:43 -04:00
74e5b83026 Merge pull request #711 from sbwalker/master
wired up JavaScript support in Module Creator templates
2020-08-27 17:17:32 -04:00
4aa0b83807 wired up JavaScript support in Module Creator templates 2020-08-27 17:16:54 -04:00
fd592e8d9f Merge pull request #707 from sbwalker/master
script file name should reflect next framework version
2020-08-26 15:25:54 -04:00
bb21eba39f script file name should reflect next major version 2020-08-26 15:24:44 -04:00
b09cb9d655 Merge pull request #50 from oqtane/master
sync
2020-08-26 15:16:28 -04:00
bbbe48b976 Merge pull request #700 from nohorse/patch-1
Create Tenant.01.00.02.02.sql
2020-08-26 15:08:35 -04:00
a036ee19a4 Merge pull request #698 from hishamco/logo
Fix logo
2020-08-26 15:02:43 -04:00
5b45c79357 Merge pull request #705 from sbwalker/master
Ensure folder does not contain files during deletion and remove directory during deletion, fix validation issue in add page which would allow a user to create a page without selecting a layout, modify action dialog to use its own CSS class name so it can be styled independently from the Admin Modal, rollback "container" CSS class assigment on panes
2020-08-26 15:01:09 -04:00
760fc3b8d4 Ensure folder does not contain files during deletion and remove directory during deletion, fix validation issue in add page which would allow a user to create a page without selecting a layout, modify action dialog to use its own CSS class name so it can be styled independently from the Admin Modal, rollback "container" CSS class assigment on panes 2020-08-26 15:00:07 -04:00
fc50a45ecd Create Tenant.01.00.02.02.sql
Proposed fixed for #699
2020-08-22 14:55:43 -07:00
e3fe8c5914 Fix logo 2020-08-22 04:19:11 +03:00
2624b9c105 Merge pull request #691 from mikecasas/plural-fix
Delete module pluralization in the location display.
2020-08-19 05:09:54 -07:00
2f9f823330 Delete module pluralization in the location display. 2020-08-18 17:02:40 -04:00
6cc144d733 Merge pull request #49 from oqtane/master
sync
2020-08-18 13:38:24 -07:00
df404c12a4 Merge pull request #686 from mikecasas/master
Add project reference for dotnet publish to work without errors.
2020-08-18 13:37:36 -07:00
faec53b3c5 Merge pull request #688 from mikecasas/patch-1
Rename MenuHorizontal.Razor to MenuHorizontal.razor
2020-08-18 13:37:23 -07:00
e1ec58b297 Rename MenuHorizontal.Razor to MenuHorizontal.razor 2020-08-18 09:34:26 -04:00
69abce7ce4 Merge pull request #19 from oqtane/master
sync master
2020-08-18 09:14:20 -04:00
38738e0844 Add project reference for dotnet publish to work without errors. 2020-08-16 22:35:09 -04:00
abe0a1a806 Merge pull request #685 from sbwalker/master
resolved #604 - added support for renaming files and moving to a different folder. Also added support for renaming folders and moving to a different parent folder.
2020-08-16 16:03:10 -07:00
809946685a resolved #604 - added support for renaming files and moving to a different folder. Also added support for renaming folders and moving to a different parent folder. 2020-08-16 19:00:49 -04:00
20c8f1528d Merge pull request #683 from sbwalker/master
resolve #526 remove pluralization from module creation templates
2020-08-14 09:44:44 -07:00
282579fcf2 resolve #526 remove pluralization from module creation templates 2020-08-14 12:44:37 -04:00
c8e3fa88e7 Merge pull request #679 from sbwalker/master
Fix #676 - fix creation of new profile fields, add support for required and private profile fields, integrate field level help for consistency
2020-08-13 07:06:28 -07:00
aec5882de1 Fix #676 - fix creation of new profile fields, add support for required and private profile fields, integrate field level help for consistency 2020-08-13 10:06:15 -04:00
bc231b18cf Update README.md 2020-08-07 14:56:58 -04:00
6bcb769fe5 Merge pull request #675 from sbwalker/master
prepare for 1.0.3 release
2020-08-07 10:40:00 -07:00
90110a653c prepare for 1.0.3 release 2020-08-07 13:39:19 -04:00
3c561cc413 Merge pull request #48 from oqtane/master
sync
2020-08-07 10:25:15 -07:00
73f9622ba2 Merge pull request #674 from sbwalker/master
prepare for 1.0.3 release
2020-08-07 10:24:31 -07:00
cf198ff781 prepare for 1.0.3 release 2020-08-07 13:23:58 -04:00
648fc56495 Merge pull request #673 from sbwalker/master
fixed very large file upload
2020-08-07 08:46:57 -07:00
ea6dc6b983 fixed very large file upload 2020-08-07 11:46:11 -04:00
c0e8d09ce1 Merge pull request #672 from sbwalker/master
allow user to reinstall current version
2020-08-06 13:48:07 -07:00
a471784cf3 allow user to reinstall current version 2020-08-06 16:46:22 -04:00
1d2a4bf484 Merge pull request #671 from sbwalker/master
include logging during module and theme installation
2020-08-06 13:37:55 -07:00
3fa620f3bc include logging during module and theme installation 2020-08-06 16:37:27 -04:00
35f186b532 Merge pull request #670 from sbwalker/master
fix regression bug caused by #649 related to installing nupkg packages
2020-08-06 13:10:51 -07:00
5cf35fd70a fix regression bug caused by #649 related to installing nupkg packages 2020-08-06 16:10:19 -04:00
1eef08eaeb Merge pull request #669 from sbwalker/master
modifications for System Update
2020-08-06 10:30:40 -07:00
1750f28a9f modifications for System Update 2020-08-06 13:30:06 -04:00
41edbc5e22 Merge pull request #668 from sbwalker/master
modifications for System Update feature
2020-08-04 10:07:35 -07:00
04257f75e7 modifications for System Update feature 2020-08-04 13:06:54 -04:00
5fb602f733 Merge pull request #667 from sbwalker/master
Improvements to System Update
2020-08-04 05:48:19 -07:00
94f0bdcce9 Improvements to System Update 2020-08-04 08:47:39 -04:00
0ba24f9a3a Update README.md 2020-08-04 08:42:28 -04:00
aac7d6b97a Merge pull request #664 from sbwalker/master
create 1.0.2 packages
2020-07-23 15:32:17 -04:00
bcc33af52b create 1.0.2 packages 2020-07-23 15:31:28 -04:00
24fd42636a Merge pull request #663 from sbwalker/master
preparing for 1.0.2 release
2020-07-23 15:08:27 -04:00
8d539d058c preparing for 1.0.2 release 2020-07-23 15:07:18 -04:00
abda377f6f Merge pull request #662 from sbwalker/master
fix regression bug caused by #648  - the entries within a nupkg (zip) package use the '/' separator - the fix in #648 was causing a wwwroot\wwwroot\... folder to be created on Windows
2020-07-23 14:59:25 -04:00
51bf822392 fix regression bug caused by #648 - the entries within a nupkg (zip) package use the '/' separator - the fix in #648 was causing a wwwroot\wwwroot\... folder to be created on Windows 2020-07-23 14:58:33 -04:00
d3f135a9c7 Merge pull request #47 from oqtane/master
sync
2020-07-23 14:41:29 -04:00
07ba99cc41 Merge pull request #661 from sbwalker/master
increase wait time for browser redirects during app restarts
2020-07-23 14:40:46 -04:00
336550c571 increase wait time for browser redirects during app restarts 2020-07-23 14:39:53 -04:00
679cc04178 Merge pull request #660 from sbwalker/master
optimize NotificationJob so that it only processes the sites for each tenant once.
2020-07-23 14:15:24 -04:00
75fe4e7c89 optimize NotificationJob so that it only processes the sites for each tenant once. 2020-07-23 14:14:29 -04:00
410f8c74e5 Merge pull request #649 from JoergH66/feature/LoadDependDlls_InstallManager
Fixes 2 external module installation problems
2020-07-23 11:39:35 -04:00
05f67d6a2a Merge pull request #659 from sbwalker/master
fixed scheduler so that it does not set NextExecution until after the job has finished executing
2020-07-23 11:39:19 -04:00
3a6cde0e24 fixed scheduler so that it does not set NextExecution until after the job has finished executing 2020-07-23 11:38:20 -04:00
fe1de2b243 Merge pull request #658 from sbwalker/master
Allow scheduled jobs to set next execution date, fix issue in site settings where logo field was not being populated, fixed compositing issue where deleted modules were being rendered.
2020-07-22 16:10:23 -04:00
62a6b5f28a Allow scheduled jobs to set next execution date, fix issue in site settings where logo field was not being populated, fixed compositing issue where deleted modules were being rendered. 2020-07-22 16:09:39 -04:00
d648fa0f02 Merge pull request #657 from ADefWebserver/master
Make a Deploy to Azure Button #168
2020-07-21 11:07:36 -04:00
e706e8cf1f Update README.md
Points the button to the Oqtane repository
2020-07-18 09:55:27 -07:00
9eb8a7e65c Update azuredeploy.json 2020-07-18 09:40:40 -07:00
11c610edf0 Update .deployment 2020-07-18 09:18:02 -07:00
a65cdbd7ad Rename azure.deployment to .deployment 2020-07-18 08:12:18 -07:00
97c56ba142 Create azure.deployment 2020-07-18 08:02:03 -07:00
9fe72a1c98 Update azuredeploy.json 2020-07-17 18:45:57 -07:00
7b40725534 Update README.md 2020-07-17 18:15:08 -07:00
5e1671afe3 Create azuredeploy.json 2020-07-17 18:10:22 -07:00
50d74cbcee Merge pull request #656 from sbwalker/master
modifications to ActionLink and ActionDialog controls, added logic to prevent IHostedService from blocking startup, made controller route prefix consistent in module template
2020-07-17 15:11:32 -04:00
bc73e5e3d0 modifications to ActionLink and ActionDialog controls, added logic to prevent IHostedService from blocking startup, made controller route prefix consistent in module template 2020-07-17 15:09:05 -04:00
b02bdee8cb Merge pull request #46 from oqtane/master
sync
2020-07-17 10:10:23 -04:00
9db4985b14 Merge pull request #655 from alexhendel/fix-path-handling
Fix directory separator for path operations
2020-07-16 10:27:34 -04:00
6f281c256b Merge pull request #654 from mikecasas/upstream-local
Should be moduleid as the entity id is added in the CreateAuthPolicyU…
2020-07-16 10:27:19 -04:00
807252c9e5 Fix directory separator for path operations 2020-07-15 16:09:19 +02:00
23e7f66188 Delete module id as it is getting added in CreateAuthPolicyUrl method. 2020-07-14 11:47:59 -04:00
57c500f4bc Merge remote-tracking branch 'github-mike/upstream-local' into upstream-local 2020-07-14 11:42:55 -04:00
fe302aa9e4 Should be moduleid as the entity id is added in the CreateAuthPolicyUrl method. 2020-07-12 18:59:20 -04:00
f14f927df7 Update README.md 2020-07-09 10:19:20 -04:00
c3f74a5217 Update README.md 2020-07-09 10:18:06 -04:00
457d1bb563 Update README.md 2020-07-09 10:17:34 -04:00
25918056cb Update README.md 2020-07-09 10:14:53 -04:00
86517dd793 Update README.md 2020-07-09 10:13:07 -04:00
00ce083a2c Update README.md 2020-07-09 10:11:28 -04:00
bce262cd8e Merge pull request #652 from sbwalker/master
remove line feeds from content during import
2020-07-09 08:46:50 -04:00
b5db62ef6a remove line feeds from content during import 2020-07-09 08:45:23 -04:00
3703d87d50 Merge pull request #651 from sbwalker/master
Html encode job log messages, add new IModule property to allow modules to specify Runtime support, provide feedback during module content import, remove default EditMode option at the Page level (should be implemented at Module level) - resolves issue where Admin modules could not be deleted, include link to Event Log in AddModuleMessage for Error message type, fixed fallback support for themes in siterouter, integrated auth policy into site templates for Module Creator
2020-07-08 19:57:13 -04:00
f515def414 Html encode job log messages, add new IModule property to allow modules to specify Runtime support, provide feedback during module content import, remove default EditMode option at the Page level (should be implemented at Module level) - resolves issue where Admin modules could not be deleted, include link to Event Log in AddModuleMessage for Error message type, fixed fallback support for themes in siterouter, integrated auth policy into site templates for Module Creator 2020-07-08 19:56:02 -04:00
4bdf20822f check whether the file is in use, dependent runtime-dlls will distribute 2020-07-08 14:06:41 +02:00
49f4e64cb4 Merge pull request #45 from oqtane/master
sync
2020-07-07 08:44:51 -04:00
e615263706 Merge pull request #647 from chlupac/Notifications
Notification changes UNDO
2020-07-07 08:43:52 -04:00
2a7e256116 Merge pull request #648 from alexhendel/fix-linux-theme-installation
Fix directory separator in InstallationManager
2020-07-07 08:43:37 -04:00
a083405b48 Fix directory seperator in InstallationManager 2020-07-07 09:56:24 +02:00
3ea280c82a Motification changes UNDO 2020-07-06 15:27:35 +02:00
192433f02d Merge pull request #44 from oqtane/master
sync
2020-07-05 11:32:20 -04:00
56a2e9dcea Merge pull request #644 from chlupac/ActionIcons
Icons in module actions menu
2020-07-03 15:16:47 -04:00
921cced1c8 Merge pull request #643 from chlupac/Notifications
Notification job optimalization
2020-07-03 15:16:14 -04:00
b17f679f38 Notification job optimalization 2020-07-03 10:19:12 +02:00
8e43fcab21 Icons in module actions menu 2020-07-03 10:15:45 +02:00
73c5092e46 Merge pull request #43 from oqtane/master
sync
2020-07-02 08:56:55 -04:00
56537e4785 Merge pull request #642 from svreic/bugfix/page-path-validation
Page path validation
2020-07-02 08:13:39 -04:00
3bd7d7196d Merge pull request #640 from PoisnFang/routing
Url parameter helper enhancements
2020-07-02 08:11:14 -04:00
7b5a192b82 Added double page path validation 2020-07-02 09:47:42 +02:00
d4be058d07 Can get parameters without template
clear urlParameters dictionary if template fails. Removed UrlParametersTemplate property and UrlParamerters auto dictionary
2020-07-01 15:15:39 -07:00
6c20fea46a Merge pull request #641 from chlupac/NotifyRepo
Notification Repository Breaking change fix
2020-07-01 14:22:26 -04:00
2e7cfefb2e Notification Repository Breaking change fix 2020-07-01 14:23:55 +02:00
038894cf64 Enhancement to url parameters helper in modulebase 2020-07-01 01:35:06 -07:00
954e30d89f Save url parameter action segments 2020-06-30 16:01:16 -07:00
93d9c4534d Merge pull request #42 from oqtane/master
sync
2020-06-30 16:56:08 -04:00
468ca8c6a9 Merge pull request #639 from PoisnFang/routing
Hot fix for homepage routing
2020-06-30 16:54:54 -04:00
e7a4c08dea Now also properly routes in admin modules 2020-06-30 13:51:48 -07:00
69d639ee42 Hot fix for homepage routing 2020-06-30 13:42:35 -07:00
a780569a6f Merge pull request #41 from oqtane/master
sync
2020-06-30 16:16:11 -04:00
568c283efd Merge pull request #638 from PoisnFang/routing
Module Router Enhancement
2020-06-30 16:15:27 -04:00
fccdd07a08 Replaced token identifiers for { } 2020-06-30 12:59:19 -07:00
5e816ea912 Removed anchor property and hash is only set if there is anchor 2020-06-30 12:49:56 -07:00
cb2d529689 added in GetUrlParameters route to Module Index action 2020-06-30 04:16:08 -07:00
c5037e7084 Url parameters working on any page, plus queries and anchors 2020-06-30 03:41:35 -07:00
fdc39d57fb Module Router Enhancement
Allows for PageVariables through the URL
2020-06-27 11:49:24 -07:00
4960e2c668 Update README.md 2020-06-26 08:45:13 -04:00
66cc3a1392 Merge pull request #637 from sbwalker/master
improvements for custom authorization policy usage
2020-06-25 10:24:54 -04:00
6e7c8e7b05 improvements for custom authorization policy usage 2020-06-25 10:23:27 -04:00
727b943fa3 Merge pull request #636 from sbwalker/master
added ModuleControlBase
2020-06-25 09:32:56 -04:00
a4a0334ec0 added ModuleControlBase 2020-06-25 09:31:21 -04:00
362 changed files with 8705 additions and 4986 deletions

2
.deployment Normal file
View File

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

53
.editorconfig Normal file
View File

@ -0,0 +1,53 @@
root = true
[*]
end_of_line = crlf
charset = utf-8
indent_style = space
indent_size = 4
[*.{json,csproj,props,targets}]
indent_size = 2
[*.cs]
# Prefer "var" everywhere
csharp_style_var_for_built_in_types = true : suggestion
csharp_style_var_when_type_is_apparent = true : suggestion
csharp_style_var_elsewhere = true : suggestion
# Newline settings
csharp_new_line_before_open_brace = all
csharp_new_line_before_else = true
csharp_new_line_before_catch = true
csharp_new_line_before_finally = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_members_in_anonymous_types = true
# Sort using and Import directives with System.* appearing first
dotnet_sort_system_directives_first = true
# Avoid "this." if not necessary
dotnet_style_qualification_for_field = false : suggestion
dotnet_style_qualification_for_property = false : suggestion
dotnet_style_qualification_for_method = false : suggestion
dotnet_style_qualification_for_event = false : suggestion
# Use language keywords instead of framework type names for type references
dotnet_style_predefined_type_for_locals_parameters_members = true : suggestion
dotnet_style_predefined_type_for_member_access = false : suggestion
# Suggest more modern language features when available
csharp_style_pattern_matching_over_is_with_cast_check = true : none
csharp_style_pattern_matching_over_as_with_null_check = true : none
csharp_style_inlined_variable_declaration = true : none
csharp_style_throw_expression = true : none
csharp_style_conditional_delegate_call = true : none
dotnet_style_object_initializer = true : suggestion
dotnet_style_collection_initializer = true : suggestion
dotnet_style_coalesce_expression = true : suggestion
dotnet_style_null_propagation = true : suggestion
dotnet_style_explicit_tuple_names = true : suggestion
trim_trailing_whitespace = true
insert_final_newline = true

3
.gitignore vendored
View File

@ -8,8 +8,11 @@ msbuild.binlog
*.binlog
*.nupkg
*.idea
Oqtane.Server/appsettings.json
Oqtane.Server/Data/*.mdf
Oqtane.Server/Data/*.ldf
/Oqtane.Server/Properties/PublishProfiles/FolderProfile.pubxml
Oqtane.Server/Content

View File

@ -1,31 +1,39 @@
@inject IInstallationService InstallationService
@inject IInstallationService InstallationService
@if (_initialized)
{
@if (!_installed)
@if (!_installation.Success)
{
<Installer />
}
else
{
<CascadingAuthenticationState>
<CascadingValue Value="@PageState">
<SiteRouter OnStateChange="@ChangeState" />
</CascadingValue>
</CascadingAuthenticationState>
@if (string.IsNullOrEmpty(_installation.Message))
{
<CascadingAuthenticationState>
<CascadingValue Value="@PageState">
<SiteRouter OnStateChange="@ChangeState" />
</CascadingValue>
</CascadingAuthenticationState>
}
else
{
<div class="app-alert">
@_installation.Message
</div>
}
}
}
@code {
private Installation _installation;
private bool _initialized;
private bool _installed;
private PageState PageState { get; set; }
protected override async Task OnParametersSetAsync()
{
var installation = await InstallationService.IsInstalled();
_installed = installation.Success;
_installation = await InstallationService.IsInstalled();
_initialized = true;
}

View File

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

View File

@ -1,4 +1,4 @@
@namespace Oqtane.Modules.Admin.Dashboard
@namespace Oqtane.Modules.Admin.Dashboard
@inherits ModuleBase
@inject IPageService PageService
@inject IUserService UserService
@ -11,7 +11,7 @@
string url = NavigateUrl(p.Path);
<div class="col-md-2 mx-auto text-center">
<NavLink class="nav-link" href="@url" Match="NavLinkMatch.All">
<h2><span class="oi oi-@p.Icon" aria-hidden="true"></span></h2>@p.Name
<h2><span class="@p.Icon" aria-hidden="true"></span></h2>@p.Name
</NavLink>
</div>
}

View File

@ -1,6 +1,7 @@
@namespace Oqtane.Modules.Admin.Error
@namespace Oqtane.Modules.Admin.Error
@inherits ModuleBase
@inject IModuleService ModuleService
@inject IStringLocalizer<Index> Localizer
@code {
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Anonymous;
@ -8,12 +9,12 @@
protected override async Task OnInitializedAsync()
{
Module module = await ModuleService.GetModuleAsync(ModuleState.ModuleId);
if (UserSecurity.IsAuthorized(PageState.User, Constants.HostRole))
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
{
string message = "A Problem Was Encountered Loading Module " + module.ModuleDefinitionName;
string message = Localizer["A Problem Was Encountered Loading Module {0}", module.ModuleDefinitionName];
AddModuleMessage(message, MessageType.Error);
}
await logger.LogCritical("Error Loading Module {Module}", module);
}
}

View File

@ -1,31 +1,32 @@
@namespace Oqtane.Modules.Admin.Files
@namespace Oqtane.Modules.Admin.Files
@using System.IO
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IFileService FileService
@inject IFolderService FolderService
@inject IStringLocalizer<Add> Localizer
<TabStrip>
<TabPanel Name="Upload" Heading="Upload Files">
<TabPanel Name="Upload" Heading="Upload Files" ResourceKey="UploadFiles">
<table class="table table-borderless">
<tr>
<td>
<Label For="upload" HelpText="Upload the file you want">Upload: </Label>
<Label For="upload" HelpText="Upload the file you want" ResourceKey="Upload">Upload: </Label>
</td>
<td>
<FileManager UploadMultiple="true" ShowFiles="false" FolderId="@_folderId" />
</td>
</tr>
</table>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@Localizer["Cancel"]</NavLink>
</TabPanel>
<TabPanel Name="Download" Heading="Download Files">
<TabPanel Name="Download" Heading="Download Files" ResourceKey="DownloadFiles">
@if (_folders != null)
{
<table class="table table-borderless">
<tr>
<td>
<Label For="url" HelpText="Enter the url of the file you wish to download">Url: </Label>
<Label For="url" HelpText="Enter the url of the file you wish to download" ResourceKey="Url">Url: </Label>
</td>
<td>
<input id="url" class="form-control" @bind="@url" />
@ -33,11 +34,11 @@
</tr>
<tr>
<td>
<Label For="folder" HelpText="Select the folder to save the file in">Folder: </Label>
<Label For="folder" HelpText="Select the folder to save the file in" ResourceKey="Folder">Folder: </Label>
</td>
<td>
<select id="folder" class="form-control" @bind="@_folderId">
<option value="-1">&lt;Select Folder&gt;</option>
<option value="-1">&lt;@Localizer["Select Folder"]&gt;</option>
@foreach (Folder folder in _folders)
{
<option value="@(folder.FolderId)">@(new string('-', folder.Level * 2))@(folder.Name)</option>
@ -46,8 +47,8 @@
</td>
</tr>
</table>
<button type="button" class="btn btn-success" @onclick="Download">Download</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink>
<button type="button" class="btn btn-success" @onclick="Download">@Localizer["Download"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@Localizer["Cancel"]</NavLink>
}
</TabPanel>
</TabStrip>
@ -73,7 +74,7 @@
{
if (url == string.Empty || _folderId == -1)
{
AddModuleMessage("You Must Enter A Url And Select A Folder", MessageType.Warning);
AddModuleMessage(Localizer["You Must Enter A Url And Select A Folder"], MessageType.Warning);
return;
}
@ -81,14 +82,14 @@
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 ;
}
{
AddModuleMessage(Localizer["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);
AddModuleMessage(Localizer["You Must Enter A Url With A Valid File Name"], MessageType.Warning);
return;
}
@ -96,12 +97,12 @@
{
await FileService.UploadFileAsync(url, _folderId);
await logger.LogInformation("File Downloaded Successfully From Url {Url}", url);
AddModuleMessage("File Downloaded Successfully From Url", MessageType.Success);
AddModuleMessage(Localizer["File Downloaded Successfully From Url"], MessageType.Success);
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Downloading File From Url {Url} {Error}", url, ex.Message);
AddModuleMessage("Error Downloading File From Url. Please Verify That The Url Is Valid.", MessageType.Error);
AddModuleMessage(Localizer["Error Downloading File From Url. Please Verify That The Url Is Valid."], MessageType.Error);
}
}
}

View File

@ -0,0 +1,112 @@
@namespace Oqtane.Modules.Admin.Files
@inherits ModuleBase
@inject IFileService FileService
@inject IFolderService FolderService
@inject NavigationManager NavigationManager
@inject IStringLocalizer<Details> Localizer
@if (_folders != null)
{
<table class="table table-borderless">
<tr>
<td>
<Label for="name" HelpText="The name of the file" ResourceKey="Name">Name: </Label>
</td>
<td>
<input id="name" class="form-control" @bind="@_name" />
</td>
</tr>
<tr>
<td>
<Label For="parent" HelpText="The folder where the file is located" ResourceKey="Folder">Folder: </Label>
</td>
<td>
<select id="parent" class="form-control" @bind="@_folderId">
@foreach (Folder folder in _folders)
{
<option value="@(folder.FolderId)">@(new string('-', folder.Level * 2))@(folder.Name)</option>
}
</select>
</td>
</tr>
<tr>
<td>
<Label for="size" HelpText="The size of the file (in bytes)" ResourceKey="Size">Size: </Label>
</td>
<td>
<input id="size" class="form-control" @bind="@_size" readonly />
</td>
</tr>
</table>
<button type="button" class="btn btn-success" @onclick="SaveFile">@Localizer["Save"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@Localizer["Cancel"]</NavLink>
<br />
<br />
<AuditInfo CreatedBy="@_createdBy" CreatedOn="@_createdOn" ModifiedBy="@_modifiedBy" ModifiedOn="@_modifiedOn"></AuditInfo>
}
@code {
private int _fileId = -1;
private string _name;
private List<Folder> _folders;
private int _folderId = -1;
private int _size;
private string _createdBy;
private DateTime _createdOn;
private string _modifiedBy;
private DateTime _modifiedOn;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
public override string Title => "File Management";
protected override async Task OnInitializedAsync()
{
try
{
_folders = await FolderService.GetFoldersAsync(PageState.Site.SiteId);
_fileId = Int32.Parse(PageState.QueryString["id"]);
File file = await FileService.GetFileAsync(_fileId);
if (file != null)
{
_name = file.Name;
_folderId = file.FolderId;
_size = file.Size;
_createdBy = file.CreatedBy;
_createdOn = file.CreatedOn;
_modifiedBy = file.ModifiedBy;
_modifiedOn = file.ModifiedOn;
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading File {FileId} {Error}", _fileId, ex.Message);
AddModuleMessage(Localizer["Error Loading File"], MessageType.Error);
}
}
private async Task SaveFile()
{
try
{
if (_name.IsPathOrFileValid())
{
File file = await FileService.GetFileAsync(_fileId);
file.Name = _name;
file.FolderId = _folderId;
file = await FileService.UpdateFileAsync(file);
await logger.LogInformation("File Saved {File}", file);
NavigationManager.NavigateTo(NavigateUrl());
}
else
{
AddModuleMessage(Localizer["File Name Not Valid"], MessageType.Warning);
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Saving File {FileId} {Error}", _fileId, ex.Message);
AddModuleMessage(Localizer["Error Saving File"], MessageType.Error);
}
}
}

View File

@ -1,20 +1,22 @@
@namespace Oqtane.Modules.Admin.Files
@namespace Oqtane.Modules.Admin.Files
@inherits ModuleBase
@inject IFolderService FolderService
@inject IFileService FileService
@inject NavigationManager NavigationManager
@inject IStringLocalizer<Edit> Localizer
@if (_folders != null)
{
<table class="table table-borderless">
<tr>
<td>
<Label For="parent" HelpText="Select the parent folder">Parent: </Label>
<Label For="parent" HelpText="Select the parent folder" ResourceKey="Parent">Parent: </Label>
</td>
<td>
<select id="parent" class="form-control" @bind="@_parentId">
@if (PageState.QueryString.ContainsKey("id"))
{
<option value="-1">&lt;No Parent&gt;</option>
<option value="-1">&lt;@Localizer["No Parent"]&gt;</option>
}
@foreach (Folder folder in _folders)
{
@ -25,7 +27,7 @@
</tr>
<tr>
<td>
<Label for="name" HelpText="Enter the folder name">Name: </Label>
<Label for="name" HelpText="Enter the folder name" ResourceKey="Name">Name: </Label>
</td>
<td>
<input id="name" class="form-control" @bind="@_name" />
@ -33,19 +35,19 @@
</tr>
<tr>
<td colspan="2" align="center">
<Label For="permissions" HelpText="Select the permissions you want for the folder">Permissions: </Label>
<PermissionGrid EntityName="@EntityNames.Folder" PermissionNames="Browse,View,Edit" Permissions="@_permissions" @ref="_permissionGrid" />
<Label For="permissions" HelpText="Select the permissions you want for the folder" ResourceKey="Permissions">Permissions: </Label>
<PermissionGrid EntityName="@EntityNames.Folder" PermissionNames="@(PermissionNames.Browse + "," + PermissionNames.View + "," + PermissionNames.Edit)" Permissions="@_permissions" @ref="_permissionGrid" />
</td>
</tr>
</table>
@if (!_isSystem)
{
<button type="button" class="btn btn-success" @onclick="SaveFolder">Save</button>
<button type="button" class="btn btn-success" @onclick="SaveFolder">@Localizer["Save"]</button>
}
<NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@Localizer["Cancel"]</NavLink>
@if (!_isSystem && PageState.QueryString.ContainsKey("id"))
{
<button type="button" class="btn btn-danger" @onclick="DeleteFolder">Delete</button>
<ActionDialog Header="Delete Folder" Message="Are You Sure You Wish To Delete This Folder?" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteFolder())" ResourceKey="DeleteFolder" />
}
<br />
<br />
@ -106,7 +108,7 @@
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading Folder {FolderId} {Error}", _folderId, ex.Message);
AddModuleMessage("Error Loading Folder", MessageType.Error);
AddModuleMessage(Localizer["Error Loading Folder"], MessageType.Error);
}
}
@ -114,16 +116,16 @@
{
if (_name == string.Empty || _parentId == -1)
{
AddModuleMessage("Folders Must Have A Parent And A Name", MessageType.Warning);
AddModuleMessage(Localizer["Folders Must Have A Parent And A Name"], MessageType.Warning);
return;
}
if (!_name.IsPathOrFileValid())
{
AddModuleMessage("Folder Name Not Valid.", MessageType.Warning);
AddModuleMessage(Localizer["Folder Name Not Valid."], MessageType.Warning);
return;
}
try
{
Folder folder;
@ -168,13 +170,13 @@
}
else
{
AddModuleMessage("An Error Was Encountered Saving The Folder", MessageType.Error);
AddModuleMessage(Localizer["An Error Was Encountered Saving The Folder"], MessageType.Error);
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Saving Folder {FolderId} {Error}", _folderId, ex.Message);
AddModuleMessage("Error Saving Module", MessageType.Error);
AddModuleMessage(Localizer["Error Saving Folder"], MessageType.Error);
}
}
@ -182,14 +184,38 @@
{
try
{
await FolderService.DeleteFolderAsync(_folderId);
await logger.LogInformation("Folder Deleted {Folder}", _folderId);
AddModuleMessage("Folder Deleted", MessageType.Success);
bool isparent = false;
foreach (Folder folder in _folders)
{
if (folder.ParentId == _folderId)
{
isparent = true;
break;
}
}
if (!isparent)
{
var files = await FileService.GetFilesAsync(_folderId);
if (files.Count == 0)
{
await FolderService.DeleteFolderAsync(_folderId);
await logger.LogInformation("Folder Deleted {Folder}", _folderId);
NavigationManager.NavigateTo(NavigateUrl());
}
else
{
AddModuleMessage(Localizer["Folder Has Files And Cannot Be Deleted"], MessageType.Warning);
}
}
else
{
AddModuleMessage(Localizer["Folder Has Subfolders And Cannot Be Deleted"], MessageType.Warning);
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Deleting Folder {Folder} {Error}", _folderId, ex.Message);
AddModuleMessage("Error Deleting Folder", MessageType.Error);
AddModuleMessage(Localizer["Error Deleting Folder"], MessageType.Error);
}
}
}

View File

@ -1,15 +1,16 @@
@namespace Oqtane.Modules.Admin.Files
@namespace Oqtane.Modules.Admin.Files
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IFolderService FolderService
@inject IFileService FileService
@inject IStringLocalizer<Index> Localizer
@if (_files != null)
{
<table class="table table-borderless">
<tr>
<td>
<label class="control-label">Folder: </label>
<label class="control-label">@Localizer["Folder:"] </label>
</td>
<td>
<select class="form-control" @onchange="(e => FolderChanged(e))">
@ -20,31 +21,33 @@
</select>
</td>
<td>
<ActionLink Action="Edit" Text="Edit Folder" Class="btn btn-secondary" Parameters="@($"id=" + _folderId.ToString())" />&nbsp;
<ActionLink Action="Edit" Text="Add Folder" Class="btn btn-secondary" />&nbsp;
<ActionLink Action="Add" Text="Upload Files" Parameters="@($"id=" + _folderId.ToString())" />
<ActionLink Action="Edit" Text="Edit Folder" Class="btn btn-secondary" Parameters="@($"id=" + _folderId.ToString())" ResourceKey="EditFolder" />&nbsp;
<ActionLink Action="Edit" Text="Add Folder" Class="btn btn-secondary" ResourceKey="AddFolder" />&nbsp;
<ActionLink Action="Add" Text="Upload Files" Parameters="@($"id=" + _folderId.ToString())" ResourceKey="UploadFiles" />
</td>
</tr>
</table>
<Pager Items="@_files">
<Header>
<th style="width: 1px;">&nbsp;</th>
<th>Name</th>
<th>Modified</th>
<th>Type</th>
<th>Size</th>
<th style="width: 1px;">&nbsp;</th>
<th>@Localizer["Name"]</th>
<th>@Localizer["Modified"]</th>
<th>@Localizer["Type"]</th>
<th>@Localizer["Size"]</th>
</Header>
<Row>
<td><ActionDialog Header="Delete File" Message="@("Are You Sure You Wish To Delete " + context.Name + "?")" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteFile(context))" /></td>
<td><ActionLink Action="Details" Text="Edit" Parameters="@($"id=" + context.FileId.ToString())" ResourceKey="Details" /></td>
<td><ActionDialog Header="Delete File" Message="@Localizer["Are You Sure You Wish To Delete {0}?", context.Name]" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteFile(context))" ResourceKey="DeleteFile" /></td>
<td><a href="@(ContentUrl(context.FileId))" target="_new">@context.Name</a></td>
<td>@context.ModifiedOn</td>
<td>@context.Extension.ToUpper() File</td>
<td>@(context.Size / 1000) KB</td>
<td>@context.Extension.ToUpper() @Localizer["File"]</td>
<td>@string.Format("{0:0.00}", ((decimal)context.Size / 1000)) KB</td>
</Row>
</Pager>
@if (_files.Count == 0)
{
<div class="text-center">No Files Exist In Selected Folder</div>
<div class="text-center">@Localizer["No Files Exist In Selected Folder"]</div>
}
}
@ -70,7 +73,7 @@
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading Files {Error}", ex.Message);
AddModuleMessage("Error Loading Files", MessageType.Error);
AddModuleMessage(Localizer["Error Loading Files"], MessageType.Error);
}
}
@ -90,7 +93,7 @@
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading Files {Error}", ex.Message);
AddModuleMessage("Error Loading Files", MessageType.Error);
AddModuleMessage(Localizer["Error Loading Files"], MessageType.Error);
}
}
@ -100,14 +103,14 @@
{
await FileService.DeleteFileAsync(file.FileId);
await logger.LogInformation("File Deleted {File}", file.Name);
AddModuleMessage("File " + file.Name + " Deleted", MessageType.Success);
AddModuleMessage(Localizer["File {0} Deleted", file.Name], MessageType.Success);
await GetFiles();
StateHasChanged();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Deleting File {File} {Error}", file.Name, ex.Message);
AddModuleMessage("Error Deleting File " + file.Name, MessageType.Error);
AddModuleMessage(Localizer["Error Deleting File {0}", file.Name], MessageType.Error);
}
}
}

View File

@ -1,140 +0,0 @@
@namespace Oqtane.Modules.Admin.Jobs
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IJobService JobService
<table class="table table-borderless">
<tr>
<td>
<Label For="name" HelpText="Enter the job name">Name: </Label>
</td>
<td>
<input id="name" class="form-control" @bind="@_name" />
</td>
</tr>
<tr>
<td>
<Label For="type" HelpText="Enter the job type">Type: </Label>
</td>
<td>
<input id="type" class="form-control" @bind="@_jobType" />
</td>
</tr>
<tr>
<td>
<Label For="enabled" HelpText="Select whether you want the job enabled or not">Enabled? </Label>
</td>
<td>
<select id="enabled" class="form-control" @bind="@_isEnabled">
<option value="True">Yes</option>
<option value="False">No</option>
</select>
</td>
</tr>
<tr>
<td>
<Label For="runs-every" HelpText="Select how often you want the job to run">Runs Every: </Label>
</td>
<td>
<input id="runs-every" class="form-control" @bind="@_interval" />
<select id="runs-every" class="form-control" @bind="@_frequency">
<option value="m">Minute(s)</option>
<option value="H">Hour(s)</option>
<option value="d">Day(s)</option>
<option value="M">Month(s)</option>
</select>
</td>
</tr>
<tr>
<td>
<Label For="starting" HelpText="What time do you want the job to start">Starting: </Label>
</td>
<td>
<input id="starting" class="form-control" @bind="@_startDate" />
</td>
</tr>
<tr>
<td>
<Label For="ending" HelpText="When do you want the job to end">Ending: </Label>
</td>
<td>
<input id="ending" class="form-control" @bind="@_endDate" />
</td>
</tr>
<tr>
<td>
<Label For="retention-log" HelpText="What items do you want in the retention log">Retention Log (Items): </Label>
</td>
<td>
<input id="retention-log" class="form-control" @bind="@_retentionHistory" />
</td>
</tr>
</table>
<button type="button" class="btn btn-success" @onclick="SaveJob">Save</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink>
@code {
private string _name = string.Empty;
private string _jobType = string.Empty;
private string _isEnabled = "True";
private string _interval = string.Empty;
private string _frequency = string.Empty;
private string _startDate = string.Empty;
private string _endDate = string.Empty;
private string _retentionHistory = "10";
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
private async Task SaveJob()
{
if (_name != string.Empty && !string.IsNullOrEmpty(_jobType) && _frequency != string.Empty && _interval != string.Empty && _retentionHistory != string.Empty)
{
var job = new Job();
job.Name = _name;
job.JobType = _jobType;
job.IsEnabled = Boolean.Parse(_isEnabled);
job.Frequency = _frequency;
job.Interval = int.Parse(_interval);
if (_startDate == string.Empty)
{
job.StartDate = null;
}
else
{
job.StartDate = DateTime.Parse(_startDate);
}
if (_endDate == string.Empty)
{
job.EndDate = null;
}
else
{
job.EndDate = DateTime.Parse(_endDate);
}
job.RetentionHistory = int.Parse(_retentionHistory);
job.IsStarted = false;
job.IsExecuting = false;
job.NextExecution = null;
try
{
job = await JobService.AddJobAsync(job);
await logger.LogInformation("Job Added {Job}", job);
NavigationManager.NavigateTo(NavigateUrl());
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Adding Job {Job} {Error}", job, ex.Message);
AddModuleMessage("Error Adding Job", MessageType.Error);
}
}
else
{
AddModuleMessage("You Must Provide The Job Name, Type, Frequency, and Retention", MessageType.Warning);
}
}
}

View File

@ -1,12 +1,13 @@
@namespace Oqtane.Modules.Admin.Jobs
@namespace Oqtane.Modules.Admin.Jobs
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IJobService JobService
@inject IStringLocalizer<Edit> Localizer
<table class="table table-borderless">
<tr>
<td>
<Label For="name" HelpText="Enter the job name">Name: </Label>
<Label For="name" HelpText="Enter the job name" ResourceKey="Name">Name: </Label>
</td>
<td>
<input id="name" class="form-control" @bind="@_name" />
@ -14,40 +15,40 @@
</tr>
<tr>
<td>
<Label For="type" HelpText="Enter the job type">Type: </Label>
<Label For="type" HelpText="The fully qualified job type name" ResourceKey="Type">Type: </Label>
</td>
<td>
<input id="type" class="form-control" @bind="@_jobType" />
<input id="type" class="form-control" @bind="@_jobType" readonly />
</td>
</tr>
<tr>
<td>
<Label For="enabled" HelpText="Select whether you want the job enabled or not">Enabled? </Label>
<Label For="enabled" HelpText="Select whether you want the job enabled or not" ResourceKey="Enabled">Enabled? </Label>
</td>
<td>
<select id="enabled" class="form-control" @bind="@_isEnabled">
<option value="True">Yes</option>
<option value="False">No</option>
<option value="True">@Localizer["Yes"]</option>
<option value="False">@Localizer["No"]</option>
</select>
</td>
</tr>
<tr>
<td>
<Label For="runs-every" HelpText="Select how often you want the job to run">Runs Every: </Label>
<Label For="runs-every" HelpText="Select how often you want the job to run" ResourceKey="RunsEvery">Runs Every: </Label>
</td>
<td>
<input id="runs-every" class="form-control" @bind="@_interval" />
<select id="runs-every" class="form-control" @bind="@_frequency">
<option value="m">Minute(s)</option>
<option value="H">Hour(s)</option>
<option value="d">Day(s)</option>
<option value="M">Month(s)</option>
<option value="m">@Localizer["Minute(s)"]</option>
<option value="H">@Localizer["Hour(s)"]</option>
<option value="d">@Localizer["Day(s)"]</option>
<option value="M">@Localizer["Month(s)"]</option>
</select>
</td>
</tr>
<tr>
<td>
<Label For="starting" HelpText="What time do you want the job to start">Starting: </Label>
<Label For="starting" HelpText="What time do you want the job to start" ResourceKey="Starting">Starting: </Label>
</td>
<td>
<input id="starting" class="form-control" @bind="@_startDate" />
@ -55,7 +56,7 @@
</tr>
<tr>
<td>
<Label For="ending" HelpText="When do you want the job to end">Ending: </Label>
<Label For="ending" HelpText="When do you want the job to end" ResourceKey="Ending">Ending: </Label>
</td>
<td>
<input id="ending" class="form-control" @bind="@_endDate" />
@ -63,15 +64,23 @@
</tr>
<tr>
<td>
<Label For="retention-log" HelpText="What items do you want in the retention log">Retention Log (Items): </Label>
<Label For="retention" HelpText="Number of log entries to retain for this job" ResourceKey="RetentionLog">Retention Log (Items): </Label>
</td>
<td>
<input id="retention-log" class="form-control" @bind="@_retentionHistory" />
<input id="retention" class="form-control" @bind="@_retentionHistory" />
</td>
</tr>
<tr>
<td>
<Label For="next" HelpText="Next execution for this job." ResourceKey="NextExecution">Next Execution: </Label>
</td>
<td>
<input id="next" class="form-control" @bind="@_nextExecution" />
</td>
</tr>
</table>
<button type="button" class="btn btn-success" @onclick="SaveJob">Save</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink>
<button type="button" class="btn btn-success" @onclick="SaveJob">@Localizer["Save"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@Localizer["Cancel"]</NavLink>
@code {
private int _jobId;
@ -83,6 +92,7 @@
private string _startDate = string.Empty;
private string _endDate = string.Empty;
private string _retentionHistory = string.Empty;
private string _nextExecution = string.Empty;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
@ -102,12 +112,13 @@
_startDate = (job.StartDate != null) ? job.StartDate.ToString() : string.Empty;
_endDate = (job.EndDate != null) ? job.EndDate.ToString() : string.Empty;
_retentionHistory = job.RetentionHistory.ToString();
_nextExecution = job.NextExecution.ToString();
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading Job {JobId} {Error}", _jobId, ex.Message);
AddModuleMessage("Error Loading Job", MessageType.Error);
AddModuleMessage(Localizer["Error Loading Job"], MessageType.Error);
}
}
@ -140,6 +151,15 @@
job.EndDate = DateTime.Parse(_endDate);
}
if (_nextExecution == string.Empty)
{
job.NextExecution = null;
}
else
{
job.NextExecution = DateTime.Parse(_nextExecution);
}
job.RetentionHistory = int.Parse(_retentionHistory);
try
@ -151,12 +171,12 @@
catch (Exception ex)
{
await logger.LogError(ex, "Error Udate Job {Job} {Error}", job, ex.Message);
AddModuleMessage("Error Updating Job", MessageType.Error);
AddModuleMessage(Localizer["Error Updating Job"], MessageType.Error);
}
}
else
{
AddModuleMessage("You Must Provide The Job Name, Type, Frequency, and Retention", MessageType.Warning);
AddModuleMessage(Localizer["You Must Provide The Job Name, Type, Frequency, and Retention"], MessageType.Warning);
}
}

View File

@ -1,46 +1,47 @@
@namespace Oqtane.Modules.Admin.Jobs
@namespace Oqtane.Modules.Admin.Jobs
@inherits ModuleBase
@inject IJobService JobService
@inject IStringLocalizer<Index> Localizer
@if (_jobs == null)
{
<p><em>Loading...</em></p>
<p><em>@Localizer["Loading..."]</em></p>
}
else
{
<ActionLink Action="Add" Text="Add Job" />
<ActionLink Action="Log" Class="btn btn-secondary" Text="View Logs" />
<ActionLink Action="Log" Class="btn btn-secondary" Text="View Logs" ResourceKey="ViewJobs" />
<button type="button" class="btn btn-secondary" @onclick="(async () => await Refresh())">Refresh</button>
<br /><br />
<br />
<br />
<Pager Items="@_jobs">
<Header>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>Name</th>
<th>Status</th>
<th>Frequency</th>
<th>Next Execution</th>
<th>@Localizer["Name"]</th>
<th>@Localizer["Status"]</th>
<th>@Localizer["Frequency"]</th>
<th>@Localizer["Next Execution"]</th>
<th style="width: 1px;">&nbsp;</th>
</Header>
<Row>
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.JobId.ToString())" /></td>
<td><ActionDialog Header="Delete Job" Message="@("Are You Sure You Wish To Delete This Job?")" Action="Delete" Security="SecurityAccessLevel.Host" Class="btn btn-danger" OnClick="@(async () => await DeleteJob(context))" /></td>
<td><ActionLink Action="Log" Class="btn btn-secondary" Parameters="@($"id=" + context.JobId.ToString())" /></td>
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.JobId.ToString())" ResourceKey="EditJob" /></td>
<td><ActionDialog Header="Delete Job" Message="Are You Sure You Wish To Delete This Job?" Action="Delete" Security="SecurityAccessLevel.Host" Class="btn btn-danger" OnClick="@(async () => await DeleteJob(context))" ResourceKey="DeleteJob" /></td>
<td><ActionLink Action="Log" Class="btn btn-secondary" Parameters="@($"id=" + context.JobId.ToString())" ResourceKey="JobLog" /></td>
<td>@context.Name</td>
<td>@DisplayStatus(context.IsEnabled, context.IsExecuting)</td>
<td>@DisplayFrequency(context.Interval, context.Frequency)</td>
<td>@context.NextExecution</td>
<td>
@if (context.IsStarted)
{
<button type="button" class="btn btn-danger" @onclick="(async () => await StopJob(context.JobId))">Stop</button>
}
else
{
<button type="button" class="btn btn-success" @onclick="(async () => await StartJob(context.JobId))">Start</button>
}
{
<button type="button" class="btn btn-danger" @onclick="(async () => await StopJob(context.JobId))">@Localizer["Stop"]</button>
}
else
{
<button type="button" class="btn btn-success" @onclick="(async () => await StartJob(context.JobId))">@Localizer["Start"]</button>
}
</td>
</Row>
</Pager>
@ -48,7 +49,7 @@ else
@code {
private List<Job> _jobs;
public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } }
protected override async Task OnParametersSetAsync()
@ -61,17 +62,17 @@ else
var status = string.Empty;
if (!isEnabled)
{
status = "Disabled";
status = Localizer["Disabled"];
}
else
{
if (isExecuting)
{
status = "Executing";
status = Localizer["Executing"];
}
else
{
status = "Idle";
status = Localizer["Idle"];
}
}
@ -81,28 +82,28 @@ else
private string DisplayFrequency(int interval, string frequency)
{
var result = "Every " + interval.ToString() + " ";
var result = $"{Localizer["Every"]} {interval.ToString()} ";
switch (frequency)
{
case "m":
result += "Minute";
result += Localizer["Minute"];
break;
case "H":
result += "Hour";
result += Localizer["Hour"];
break;
case "d":
result += "Day";
result += Localizer["Day"];
break;
case "M":
result += "Month";
result += Localizer["Month"];
break;
}
if (interval > 1)
{
result += "s";
result += Localizer["s"];
}
return result;
}
@ -117,7 +118,7 @@ else
catch (Exception ex)
{
await logger.LogError(ex, "Error Deleting Job {Job} {Error}", job, ex.Message);
AddModuleMessage("Error Deleting Job", MessageType.Error);
AddModuleMessage(Localizer["Error Deleting Job"], MessageType.Error);
}
}

View File

@ -1,19 +1,20 @@
@namespace Oqtane.Modules.Admin.Jobs
@namespace Oqtane.Modules.Admin.Jobs
@inherits ModuleBase
@inject IJobLogService JobLogService
@inject IStringLocalizer<Log> Localizer
@if (_jobLogs == null)
{
<p><em>Loading...</em></p>
<p><em>@Localizer["Loading..."]</em></p>
}
else
{
<Pager Items="@_jobLogs">
<Header>
<th>Name</th>
<th>Status</th>
<th>Started</th>
<th>Finished</th>
<th>@Localizer["Name"]</th>
<th>@Localizer["Status"]</th>
<th>@Localizer["Started"]</th>
<th>@Localizer["Finished"]</th>
</Header>
<Row>
<td>@context.Job.Name</td>
@ -22,7 +23,7 @@ else
<td>@context.FinishDate</td>
</Row>
<Detail>
<td colspan="4">@context.Notes</td>
<td colspan="4">@((MarkupString)context.Notes)</td>
</Detail>
</Pager>
}
@ -49,17 +50,17 @@ else
var status = string.Empty;
if (isExecuting)
{
status = "Executing";
status = Localizer["Executing"];
}
else
{
if (succeeded != null && succeeded.Value)
{
status = "Succeeded";
status = Localizer["Succeeded"];
}
else
{
status = "Failed";
status = Localizer["Failed"];
}
}

View File

@ -0,0 +1,107 @@
@namespace Oqtane.Modules.Admin.Languages
@inherits ModuleBase
@using System.Globalization
@using Microsoft.AspNetCore.Localization
@inject NavigationManager NavigationManager
@inject ILocalizationService LocalizationService
@inject ILanguageService LanguageService
@inject IStringLocalizer<Add> Localizer
@if (_supportedCultures == null)
{
<p><em>@Localizer["Loading..."]</em></p>
}
else
{
@if (_supportedCultures?.Count() > 1)
{
<table class="table table-borderless">
<tr>
<td>
<Label For="name" HelpText="Name Of The Langauage" ResourceKey="Name">Name:</Label>
</td>
<td>
<select id="_code" class="form-control" @bind="@_code">
@foreach (var culture in _supportedCultures)
{
<option value="@culture.Name">@culture.DisplayName</option>
}
</select>
</td>
</tr>
<tr>
<td>
<Label For="default" HelpText="Indicates Whether Or Not This Language Is The Default For The Site" ResourceKey="IsDefault">Default?</Label>
</td>
<td>
<select id="default" class="form-control" @bind="@_isDefault">
<option value="True">@Localizer["Yes"]</option>
<option value="False">@Localizer["No"]</option>
</select>
</td>
</tr>
</table>
<button type="button" class="btn btn-success" @onclick="SaveLanguage">@Localizer["Save"]</button>
}
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@Localizer["Cancel"]</NavLink>
}
@code {
private string _code = string.Empty;
private string _isDefault = "False";
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
private IEnumerable<Culture> _supportedCultures;
protected override async Task OnParametersSetAsync()
{
_supportedCultures = await LocalizationService.GetCulturesAsync();
if (_supportedCultures.Count() <= 1)
{
AddModuleMessage(Localizer["The Only Supported Culture That Has Been Defined Is English"], MessageType.Warning);
}
}
private async Task SaveLanguage()
{
var language = new Language
{
SiteId = PageState.Page.SiteId,
Name = CultureInfo.GetCultureInfo(_code).DisplayName,
Code = _code,
IsDefault = (_isDefault == null ? false : Boolean.Parse(_isDefault))
};
try
{
language = await LanguageService.AddLanguageAsync(language);
if (language.IsDefault)
{
await SetCultureAsync(language.Code);
}
await logger.LogInformation("Language Added {Language}", language);
NavigationManager.NavigateTo(NavigateUrl());
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Adding Language {Language} {Error}", language, ex.Message);
AddModuleMessage(Localizer["Error Adding Language"], MessageType.Error);
}
}
private async Task SetCultureAsync(string culture)
{
if (culture != CultureInfo.CurrentUICulture.Name)
{
var interop = new Interop(JSRuntime);
var localizationCookieValue = CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture));
await interop.SetCookie(CookieRequestCultureProvider.DefaultCookieName, localizationCookieValue, 360);
NavigationManager.NavigateTo(NavigationManager.Uri, forceLoad: true);
}
}
}

View File

@ -0,0 +1,56 @@
@namespace Oqtane.Modules.Admin.Languages
@inherits ModuleBase
@inject ILanguageService LanguageService
@inject IStringLocalizer<Index> Localizer
@if (_languages == null)
{
<p><em>@Localizer["Loading..."]</em></p>
}
else
{
<ActionLink Action="Add" Text="Add Language" ResourceKey="AddLanguage" />
<Pager Items="@_languages">
<Header>
<th style="width: 1px;">&nbsp;</th>
<th>@Localizer["Name"]</th>
<th>@Localizer["Code"]</th>
<th>@Localizer["Default?"]</th>
</Header>
<Row>
<td><ActionDialog Header="Delete Langauge" Message="@Localizer["Are You Sure You Wish To Delete The {0} Language?", context.Name]" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteLanguage(context))" Disabled="@(context.IsDefault)" ResourceKey="DeleteLanguage" /></td>
<td>@context.Name</td>
<td>@context.Code</td>
<td><TriStateCheckBox Value="@(context.IsDefault)" Disabled="true"></TriStateCheckBox></td>
</Row>
</Pager>
}
@code {
private List<Language> _languages;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
protected override async Task OnParametersSetAsync()
{
_languages = await LanguageService.GetLanguagesAsync(PageState.Site.SiteId);
}
private async Task DeleteLanguage(Language language)
{
try
{
await LanguageService.DeleteLanguageAsync(language.LanguageId);
await logger.LogInformation("Language Deleted {Language}", language);
StateHasChanged();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Deleting Language {Language} {Error}", language, ex.Message);
AddModuleMessage(Localizer["Error Deleting Language"], MessageType.Error);
}
}
}

View File

@ -1,41 +1,38 @@
@namespace Oqtane.Modules.Admin.Login
@namespace Oqtane.Modules.Admin.Login
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IUserService UserService
@inject IServiceProvider ServiceProvider
@inject IStringLocalizer<Index> Localizer
@if (_message != string.Empty)
{
<ModuleMessage Message="@_message" Type="@_type" />
}
<AuthorizeView>
<Authorizing>
<text>...</text>
</Authorizing>
<Authorized>
<ModuleMessage Message="You Are Already Logged In" Type="MessageType.Info" />
</Authorized>
<NotAuthorized>
<div class="container">
<div class="form-group">
<label for="Username" class="control-label">Username: </label>
<input type="text" name="Username" class="form-control" placeholder="Username" @bind="@_username" id="Username" />
</div>
<div class="form-group">
<label for="Password" class="control-label">Password: </label>
<input type="password" name="Password" class="form-control" placeholder="Password" @bind="@_password" id="Password" />
</div>
<div class="form-group">
<div class="form-check form-check-inline">
<label class="form-check-label" for="Remember">Remember Me?</label>&nbsp;
<input type="checkbox" class="form-check-input" name="Remember" @bind="@_remember" id="Remember" />
<form @ref="login" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
<div class="container Oqtane-Modules-Admin-Login" @onkeypress="@(e => KeyPressed(e))">
<div class="form-group">
<label for="Username" class="control-label">@Localizer["Username:"] </label>
<input type="text" @ref="username" name="Username" class="form-control username" placeholder="Username" @bind="@_username" id="Username" required />
</div>
<div class="form-group">
<label for="Password" class="control-label">@Localizer["Password:"] </label>
<input type="password" name="Password" class="form-control password" placeholder="Password" @bind="@_password" id="Password" required />
</div>
<div class="form-group">
<div class="form-check form-check-inline">
<label class="form-check-label" for="Remember">@Localizer["Remember Me?"]</label>&nbsp;
<input type="checkbox" class="form-check-input" name="Remember" @bind="@_remember" id="Remember" />
</div>
</div>
<button type="button" class="btn btn-primary" @onclick="Login">@Localizer["Login"]</button>
<button type="button" class="btn btn-secondary" @onclick="Cancel">@Localizer["Cancel"]</button>
<br /><br />
<button type="button" class="btn btn-secondary" @onclick="Forgot">@Localizer["Forgot Password"]</button>
</div>
<button type="button" class="btn btn-primary" @onclick="Login">Login</button>
<button type="button" class="btn btn-secondary" @onclick="Cancel">Cancel</button>
<br /><br />
<button type="button" class="btn btn-secondary" @onclick="Forgot">Forgot Password</button>
</div>
</form>
</NotAuthorized>
</AuthorizeView>
@ -46,21 +43,30 @@
private string _username = string.Empty;
private string _password = string.Empty;
private bool _remember = false;
private bool validated = false;
private ElementReference login;
private ElementReference username;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Anonymous;
public override List<Resource> Resources => new List<Resource>()
{
new Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css" }
};
protected override async Task OnInitializedAsync()
{
if (PageState.QueryString.ContainsKey("returnurl"))
{
_returnUrl = PageState.QueryString["returnurl"];
}
if (PageState.QueryString.ContainsKey("name"))
{
_username = PageState.QueryString["name"];
}
if (PageState.QueryString.ContainsKey("token"))
{
var user = new User();
@ -70,62 +76,78 @@
if (user != null)
{
_message = "User Account Verified Successfully. You Can Now Login With Your Username And Password Below.";
_message = Localizer["User Account Verified Successfully. You Can Now Login With Your Username And Password Below."];
}
else
{
_message = "User Account Could Not Be Verified. Please Contact Your Administrator For Further Instructions.";
_message = Localizer["User Account Could Not Be Verified. Please Contact Your Administrator For Further Instructions."];
_type = MessageType.Warning;
}
}
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await username.FocusAsync();
}
}
private async Task Login()
{
if (PageState.Runtime == Runtime.Server)
validated = true;
var interop = new Interop(JSRuntime);
if (await interop.FormValid(login))
{
// server-side Blazor
var user = new User();
user.SiteId = PageState.Site.SiteId;
user.Username = _username;
user.Password = _password;
user = await UserService.LoginUserAsync(user, false, false);
if (user.IsAuthenticated)
if (PageState.Runtime == Oqtane.Shared.Runtime.Server)
{
await logger.LogInformation("Login Successful For Username {Username}", _username);
// complete the login on the server so that the cookies are set correctly on SignalR
var interop = new Interop(JSRuntime);
string antiforgerytoken = await interop.GetElementByName("__RequestVerificationToken");
var fields = new { __RequestVerificationToken = antiforgerytoken, username = _username, password = _password, remember = _remember, returnurl = _returnUrl };
await interop.SubmitForm($"/{PageState.Alias.AliasId}/pages/login/", fields);
// server-side Blazor
var user = new User();
user.SiteId = PageState.Site.SiteId;
user.Username = _username;
user.Password = _password;
user = await UserService.LoginUserAsync(user, false, false);
if (user.IsAuthenticated)
{
await logger.LogInformation("Login Successful For Username {Username}", _username);
// complete the login on the server so that the cookies are set correctly on SignalR
string antiforgerytoken = await interop.GetElementByName("__RequestVerificationToken");
var fields = new { __RequestVerificationToken = antiforgerytoken, username = _username, password = _password, remember = _remember, returnurl = _returnUrl };
await interop.SubmitForm($"/{PageState.Alias.AliasId}/pages/login/", fields);
}
else
{
await logger.LogInformation("Login Failed For Username {Username}", _username);
AddModuleMessage(Localizer["Login Failed. Please Remember That Passwords Are Case Sensitive And User Accounts Require Verification When They Are Initially Created So You May Wish To Check Your Email."], MessageType.Error);
}
}
else
{
await logger.LogInformation("Login Failed For Username {Username}", _username);
AddModuleMessage("Login Failed. Please Remember That Passwords Are Case Sensitive And User Accounts Require Email Verification When They Initially Created.", MessageType.Error);
// client-side Blazor
var user = new User();
user.SiteId = PageState.Site.SiteId;
user.Username = _username;
user.Password = _password;
user = await UserService.LoginUserAsync(user, true, _remember);
if (user.IsAuthenticated)
{
await logger.LogInformation("Login Successful For Username {Username}", _username);
var authstateprovider = (IdentityAuthenticationStateProvider)ServiceProvider.GetService(typeof(IdentityAuthenticationStateProvider));
authstateprovider.NotifyAuthenticationChanged();
NavigationManager.NavigateTo(NavigateUrl(_returnUrl, "reload"));
}
else
{
await logger.LogInformation("Login Failed For Username {Username}", _username);
AddModuleMessage(Localizer["Login Failed. Please Remember That Passwords Are Case Sensitive And User Accounts Require Verification When They Are Initially Created So You May Wish To Check Your Email."], MessageType.Error);
}
}
}
else
{
// client-side Blazor
var user = new User();
user.SiteId = PageState.Site.SiteId;
user.Username = _username;
user.Password = _password;
user = await UserService.LoginUserAsync(user, true, _remember);
if (user.IsAuthenticated)
{
await logger.LogInformation("Login Successful For Username {Username}", _username);
var authstateprovider = (IdentityAuthenticationStateProvider)ServiceProvider.GetService(typeof(IdentityAuthenticationStateProvider));
authstateprovider.NotifyAuthenticationChanged();
NavigationManager.NavigateTo(NavigateUrl(_returnUrl, "reload"));
}
else
{
await logger.LogInformation("Login Failed For Username {Username}", _username);
AddModuleMessage("Login Failed. Please Remember That Passwords Are Case Sensitive And User Accounts Require Verification When They Are Initially Created So You May Wish To Check Your Email.", MessageType.Error);
}
AddModuleMessage(Localizer["Please Provide Your Username And Password"], MessageType.Warning);
}
}
@ -154,7 +176,15 @@
{
_message = "Please Enter The Username Related To Your Account And Then Select The Forgot Password Option Again";
}
StateHasChanged();
}
private async Task KeyPressed(KeyboardEventArgs e)
{
if (e.Code == "Enter" || e.Code == "NumpadEnter")
{
await Login();
}
}
}

View File

@ -1,4 +1,4 @@
@namespace Oqtane.Modules.Admin.Logs
@namespace Oqtane.Modules.Admin.Logs
@using System.Globalization
@inherits ModuleBase
@inject NavigationManager NavigationManager
@ -6,134 +6,135 @@
@inject IPageService PageService
@inject IPageModuleService PageModuleService
@inject IUserService UserService
@inject IStringLocalizer<Index> Localizer
<table class="table table-borderless">
<table class="table table-borderless">
<tr>
<td>
<Label For="dateTime" HelpText="The date and time of this log" ResourceKey="DateTime">Date/Time: </Label>
</td>
<td>
<input id="dateTime" class="form-control" @bind="@_logDate" readonly />
</td>
</tr>
<tr>
<td>
<Label For="level" HelpText="The level of this log" ResourceKey="Level">Level: </Label>
</td>
<td>
<input id="level" class="form-control" @bind="@_level" readonly />
</td>
</tr>
<tr>
<td>
<Label For="feature" HelpText="The feature that was affected" ResourceKey="Feature">Feature: </Label>
</td>
<td>
<input id="feature" class="form-control" @bind="@_feature" readonly />
</td>
</tr>
<tr>
<td>
<Label For="function" HelpText="The function that was performed" ResourceKey="Function">Function: </Label>
</td>
<td>
<input id="function" class="form-control" @bind="@_function" readonly />
</td>
</tr>
<tr>
<td>
<Label For="category" HelpText="The categories that were affected" ResourceKey="Category">Category: </Label>
</td>
<td>
<input id="category" class="form-control" @bind="@_category" readonly />
</td>
</tr>
@if (_pageName != string.Empty)
{
<tr>
<td>
<Label For="dateTime" HelpText="The date and time of this log">Date/Time: </Label>
<Label For="page" HelpText="The page that was affected" ResourceKey="Page">Page: </Label>
</td>
<td>
<input id="dateTime" class="form-control" @bind="@_logDate" readonly />
<input id="page" class="form-control" @bind="@_pageName" readonly />
</td>
</tr>
}
@if (_moduleTitle != string.Empty)
{
<tr>
<td>
<Label For="level" HelpText="The level of this log">Level: </Label>
<Label For="module" HelpText="The module that was affected" ResourceKey="Module">Module: </Label>
</td>
<td>
<input id="level" class="form-control" @bind="@_level" readonly />
<input id="module" class="form-control" @bind="@_moduleTitle" readonly />
</td>
</tr>
}
@if (_username != string.Empty)
{
<tr>
<td>
<Label For="feature" HelpText="The feature that was affected">Feature: </Label>
<Label For="user" HelpText="The user that caused this log" ResourceKey="User">User: </Label>
</td>
<td>
<input id="feature" class="form-control" @bind="@_feature" readonly />
<input id="user" class="form-control" @bind="@_username" readonly />
</td>
</tr>
}
<tr>
<td>
<Label For="url" HelpText="The url the log comes from" ResourceKey="Url">Url: </Label>
</td>
<td>
<input id="url" class="form-control" @bind="@_url" readonly />
</td>
</tr>
<tr>
<td>
<Label For="template" HelpText="What the log is about" ResourceKey="Template">Template: </Label>
</td>
<td>
<input id="template" class="form-control" @bind="@_template" readonly />
</td>
</tr>
<tr>
<td>
<Label For="message" HelpText="The message that the system generated" class="control-label" ResourceKey="Message">Message: </Label>
</td>
<td>
<textarea id="message" class="form-control" @bind="@_message" rows="5" readonly></textarea>
</td>
</tr>
@if (!string.IsNullOrEmpty(_exception))
{
<tr>
<td>
<Label For="function" HelpText="The function that was performed">Function: </Label>
<Label For="exception" HelpText="The exceptions generated by the system" ResourceKey="Exception">Exception: </Label>
</td>
<td>
<input id="function" class="form-control" @bind="@_function" readonly />
<textarea id="exception" class="form-control" @bind="@_exception" rows="5" readonly></textarea>
</td>
</tr>
<tr>
<td>
<Label For="category" HelpText="The categories that were affected">Category: </Label>
</td>
<td>
<input id="category" class="form-control" @bind="@_category" readonly />
</td>
</tr>
@if (_pageName != string.Empty)
{
<tr>
<td>
<Label For="page" HelpText="The page that was affected">Page: </Label>
</td>
<td>
<input id="page" class="form-control" @bind="@_pageName" readonly />
</td>
</tr>
}
@if (_moduleTitle != string.Empty)
{
<tr>
<td>
<Label For="module" HelpText="The module that was affected">Module: </Label>
</td>
<td>
<input id="module" class="form-control" @bind="@_moduleTitle" readonly />
</td>
</tr>
}
@if (_username != string.Empty)
{
<tr>
<td>
<Label For="user" HelpText="The user that caused this log">User: </Label>
</td>
<td>
<input id="user" class="form-control" @bind="@_username" readonly />
</td>
</tr>
}
<tr>
<td>
<Label For="url" HelpText="The url the log comes from">Url: </Label>
</td>
<td>
<input id="url" class="form-control" @bind="@_url" readonly />
</td>
</tr>
<tr>
<td>
<Label For="template" HelpText="What the log is about">Template: </Label>
</td>
<td>
<input id="template" class="form-control" @bind="@_template" readonly />
</td>
</tr>
<tr>
<td>
<Label For="message" HelpText="The message that the system generated"class="control-label">Message: </Label>
</td>
<td>
<textarea id="message" class="form-control" @bind="@_message" rows="5" readonly></textarea>
</td>
</tr>
@if (!string.IsNullOrEmpty(_exception))
{
<tr>
<td>
<Label For="exception" HelpText="The exceptions generated by the system">Exception: </Label>
</td>
<td>
<textarea id="exception" class="form-control" @bind="@_exception" rows="5" readonly></textarea>
</td>
</tr>
}
<tr>
<td>
<Label For="properties" HelpText="The properties that were affected">Properties: </Label>
</td>
<td>
<textarea id="properties" class="form-control" @bind="@_properties" rows="5" readonly></textarea>
</td>
</tr>
<tr>
<td>
<Label For="server" HelpText="The server that was affected">Server: </Label>
</td>
<td>
<input id="server" class="form-control" @bind="@_server" readonly />
</td>
</tr>
</table>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink>
}
<tr>
<td>
<Label For="properties" HelpText="The properties that were affected" ResourceKey="Properties">Properties: </Label>
</td>
<td>
<textarea id="properties" class="form-control" @bind="@_properties" rows="5" readonly></textarea>
</td>
</tr>
<tr>
<td>
<Label For="server" HelpText="The server that was affected" ResourceKey="Server">Server: </Label>
</td>
<td>
<input id="server" class="form-control" @bind="@_server" readonly />
</td>
</tr>
</table>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@Localizer["Cancel"]</NavLink>
@code {
private int _logId;
@ -167,7 +168,7 @@
_feature = log.Feature;
_function = log.Function;
_category = log.Category;
if (log.PageId != null)
{
var page = await PageService.GetPageAsync(log.PageId.Value);
@ -176,7 +177,7 @@
_pageName = page.Name;
}
}
if (log.PageId != null && log.ModuleId != null)
{
var pagemodule = await PageModuleService.GetPageModuleAsync(log.PageId.Value, log.ModuleId.Value);
@ -185,7 +186,7 @@
_moduleTitle = pagemodule.Title;
}
}
if (log.UserId != null)
{
var user = await UserService.GetUserAsync(log.UserId.Value, PageState.Site.SiteId);
@ -194,7 +195,7 @@
_username = user.Username;
}
}
_url = log.Url;
_template = log.MessageTemplate;
_message = log.Message;
@ -206,7 +207,7 @@
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading Log {LogId} {Error}", _logId, ex.Message);
AddModuleMessage("Error Loading Log", MessageType.Error);
AddModuleMessage(Localizer["Error Loading Log"], MessageType.Error);
}
}
}

View File

@ -1,42 +1,43 @@
@namespace Oqtane.Modules.Admin.Logs
@namespace Oqtane.Modules.Admin.Logs
@inherits ModuleBase
@inject ILogService LogService
@inject IStringLocalizer<Index> Localizer
@if (_logs == null)
{
<p><em>Loading...</em></p>
<p><em>@Localizer["Loading..."]</em></p>
}
else
{
<table class="table table-borderless">
<tr>
<td>
<label>Level: </label>
<select class="form-control" @onchange="(e => LevelChanged(e))">
<option value="-">&lt;All Levels&gt;</option>
<option value="Trace">Trace</option>
<option value="Debug">Debug</option>
<option value="Information">Information</option>
<option value="Warning">Warning</option>
<option value="Error">Error</option>
<option value="Critical">Critical</option>
<Label For="level" HelpText="Select the log level for event log items" ResourceKey="Level">Level: </Label><br /><br />
<select id="level" class="form-control" @onchange="(e => LevelChanged(e))">
<option value="-">&lt;@Localizer["All Levels"]&gt;</option>
<option value="Trace">@Localizer["Trace"]</option>
<option value="Debug">@Localizer["Debug"]</option>
<option value="Information">@Localizer["Information"]</option>
<option value="Warning">@Localizer["Warning"]</option>
<option value="Error">@Localizer["Error"]</option>
<option value="Critical">@Localizer["Critical"]</option>
</select>
</td>
<td>
<label>Function: </label>
<select class="form-control" @onchange="(e => FunctionChanged(e))">
<option value="-">&lt;All Functions&gt;</option>
<option value="Create">Create</option>
<option value="Read">Read</option>
<option value="Update">Update</option>
<option value="Delete">Delete</option>
<option value="Security">Security</option>
<option value="Other">Other</option>
<Label For="function" HelpText="Select the function for event log items" ResourceKey="Function">Function: </Label><br /><br />
<select id="function" class="form-control" @onchange="(e => FunctionChanged(e))">
<option value="-">&lt;@Localizer["All Functions"]&gt;</option>
<option value="Create">@Localizer["Create"]</option>
<option value="Read">@Localizer["Read"]</option>
<option value="Update">@Localizer["Update"]</option>
<option value="Delete">@Localizer["Delete"]</option>
<option value="Security">@Localizer["Security"]</option>
<option value="Other">@Localizer["Other"]</option>
</select>
</td>
<td>
<label>Rows: </label>
<select class="form-control" @onchange="(e => RowsChanged(e))">
<Label For="rows" HelpText="Select the maximum number of event log items to review. Please note that if you choose more than 10 items the information will be split into pages." ResourceKey="Rows">Maximum Items: </Label><br /><br />
<select id="rows" class="form-control" @onchange="(e => RowsChanged(e))">
<option value="10">10</option>
<option value="50">50</option>
<option value="100">100</option>
@ -50,13 +51,13 @@ else
<Pager Items="@_logs">
<Header>
<th style="width: 1px;">&nbsp;</th>
<th>Date</th>
<th>Level</th>
<th>Feature</th>
<th>Function</th>
<th>@Localizer["Date"]</th>
<th>@Localizer["Level"]</th>
<th>@Localizer["Feature"]</th>
<th>@Localizer["Function"]</th>
</Header>
<Row>
<td class="@GetClass(context.Function)"><ActionLink Action="Detail" Parameters="@($"id=" + context.LogId.ToString())" /></td>
<td class="@GetClass(context.Function)"><ActionLink Action="Detail" Parameters="@($"id=" + context.LogId.ToString())" ResourceKey="LogDetails" /></td>
<td class="@GetClass(context.Function)">@context.LogDate</td>
<td class="@GetClass(context.Function)">@context.Level</td>
<td class="@GetClass(context.Function)">@context.Feature</td>
@ -66,7 +67,7 @@ else
}
else
{
<p><em>No Logs Match The Criteria Specified</em></p>
<p><em>@Localizer["No Logs Match The Criteria Specified"]</em></p>
}
}
@ -87,7 +88,7 @@ else
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading Logs {Error}", ex.Message);
AddModuleMessage("Error Loading Logs", MessageType.Error);
AddModuleMessage(Localizer["Error Loading Logs"], MessageType.Error);
}
}
@ -102,7 +103,7 @@ else
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading Logs {Error}", ex.Message);
AddModuleMessage("Error Loading Logs", MessageType.Error);
AddModuleMessage(Localizer["Error Loading Logs"], MessageType.Error);
}
}
@ -117,7 +118,7 @@ else
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading Logs {Error}", ex.Message);
AddModuleMessage("Error Loading Logs", MessageType.Error);
AddModuleMessage(Localizer["Error Loading Logs"], MessageType.Error);
}
}
@ -133,7 +134,7 @@ else
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading Logs {Error}", ex.Message);
AddModuleMessage("Error Loading Logs", MessageType.Error);
AddModuleMessage(Localizer["Error Loading Logs"], MessageType.Error);
}
}

View File

@ -1,88 +1,148 @@
@namespace Oqtane.Modules.Admin.ModuleCreator
@namespace Oqtane.Modules.Admin.ModuleCreator
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IModuleDefinitionService ModuleDefinitionService
@inject IModuleService ModuleService
@inject ISystemService SystemService
@inject ISettingService SettingService
@inject IStringLocalizer<Index> Localizer
@using System.Text.RegularExpressions
@using System.IO;
<table class="table table-borderless">
<tr>
<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>
</td>
<td>
<input id="owner" class="form-control" @bind="@_owner" />
</td>
</tr>
<tr>
<td>
<Label For="module" HelpText="Enter a name for this module. It should be in singular form (ie. Car) and not contain spaces or punctuation.">Module Name: </Label>
</td>
<td>
<input id="module" class="form-control" @bind="@_module" />
</td>
</tr>
<tr>
<td>
<Label For="description" HelpText="Enter s short description for the module">Description: </Label>
</td>
<td>
<textarea id="description" class="form-control" @bind="@_description" rows="3"></textarea>
</td>
</tr>
<tr>
<td>
<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>
<select id="template" class="form-control" @onchange="(e => TemplateChanged(e))">
<option value="-">&lt;Select Template&gt;</option>
<option value="internal">Internal</option>
<option value="external">External</option>
</select>
</td>
</tr>
@if (!string.IsNullOrEmpty(_location))
{
@if (string.IsNullOrEmpty(_moduledefinitionname) && _systeminfo != null && _templates != null)
{
<table class="table table-borderless">
<tr>
<td>
<Label For="location" HelpText="Location where the module will be created">Location: </Label>
<Label For="owner" HelpText="Enter the name of the organization who is developing this module. It should not contain spaces or punctuation." ResourceKey="OwnerName">Owner Name: </Label>
</td>
<td>
<input id="module" class="form-control" @bind="@_location" readonly />
<input id="owner" class="form-control" @bind="@_owner" />
</td>
</tr>
}
</table>
<button type="button" class="btn btn-success" @onclick="CreateModule">Create Module</button>
<tr>
<td>
<Label For="module" HelpText="Enter a name for this module. It should not contain spaces or punctuation." ResourceKey="ModuleName">Module Name: </Label>
</td>
<td>
<input id="module" class="form-control" @bind="@_module" />
</td>
</tr>
<tr>
<td>
<Label For="description" HelpText="Enter a short description for the module" ResourceKey="Description">Description: </Label>
</td>
<td>
<textarea id="description" class="form-control" @bind="@_description" rows="3"></textarea>
</td>
</tr>
<tr>
<td>
<Label For="template" HelpText="Select a module template. Templates are located in the wwwroot/Modules/Templates folder on the server." ResourceKey="Template">Template: </Label>
</td>
<td>
<select id="template" class="form-control" @onchange="(e => TemplateChanged(e))">
<option value="-">&lt;@Localizer["Select Template"]&gt;</option>
@foreach (string template in _templates)
{
<option value="@template">@template</option>
}
</select>
</td>
</tr>
<tr>
<td>
<Label For="reference" HelpText="Select a framework reference version" ResourceKey="FrameworkReference">Framework Reference: </Label>
</td>
<td>
<select id="reference" class="form-control" @bind="@_reference">
@foreach (string version in Constants.ReleaseVersions.Split(','))
{
if (Version.Parse(version).CompareTo(Version.Parse("2.0.0")) >= 0)
{
<option value="@(version)">@(version)</option>
}
}
<option value="local">@Localizer["Local Version"]</option>
</select>
</td>
</tr>
@if (!string.IsNullOrEmpty(_location))
{
<tr>
<td>
<Label For="location" HelpText="Location where the module will be created" ResourceKey="Location">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">@Localizer["Create Module"]</button>
}
else
{
<button type="button" class="btn btn-success" @onclick="ActivateModule">@Localizer["Activate Module"]</button>
}
@code {
private string _moduledefinitionname = string.Empty;
private string _owner = string.Empty;
private string _module = string.Empty;
private string _description = string.Empty;
private string _template = "-";
private string _reference = Constants.Version;
private string _location = string.Empty;
private Dictionary<string, string> _systeminfo;
private List<string> _templates;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
protected override void OnInitialized()
protected override async Task OnParametersSetAsync()
{
AddModuleMessage("Please Note That Once You Select The Create Module Button The Application Must Restart In Order To Complete The Process. If You Create An External Module You Will Need To Compile The Source Code In Order To Make It Functional.", MessageType.Info);
try
{
_moduledefinitionname = SettingService.GetSetting(ModuleState.Settings, "ModuleDefinitionName", "");
_systeminfo = await SystemService.GetSystemInfoAsync();
_templates = await ModuleDefinitionService.GetModuleDefinitionTemplatesAsync();
if (string.IsNullOrEmpty(_moduledefinitionname))
{
AddModuleMessage(Localizer["Please Note That The Module Creator Is Only Intended To Be Used In A Development Environment"], MessageType.Info);
}
else
{
AddModuleMessage(Localizer["Once You Have Compiled The Module And Restarted The Application You Can Activate The Module Below"], MessageType.Info);
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading Module Creator");
}
}
private async Task CreateModule()
{
try
{
if (!string.IsNullOrEmpty(_owner) && !string.IsNullOrEmpty(_module) && _template != "-")
if (IsValid(_owner) && IsValid(_module) && _owner != _module && _template != "-")
{
var moduleDefinition = new ModuleDefinition { Owner = _owner.Replace(" ", ""), Name = _module.Replace(" ", ""), Description = _description, Template = _template };
await ModuleDefinitionService.CreateModuleDefinitionAsync(moduleDefinition, ModuleState.ModuleId);
var moduleDefinition = new ModuleDefinition { Owner = _owner, Name = _module, Description = _description, Template = _template, Version = _reference };
moduleDefinition = await ModuleDefinitionService.CreateModuleDefinitionAsync(moduleDefinition);
var settings = ModuleState.Settings;
SettingService.SetSetting(settings, "ModuleDefinitionName", moduleDefinition.ModuleDefinitionName);
await SettingService.UpdateModuleSettingsAsync(settings, ModuleState.ModuleId);
GetLocation();
AddModuleMessage(Localizer["The Source Code For Your Module Has Been Created At The Location Specified Below And Must Be Compiled In Order To Make It Functional. Once It Has Been Compiled You Must <a href=\"{0}\">Restart</a> Your Application To Apply These Changes.", NavigateUrl("admin/system")], MessageType.Success);
}
else
{
AddModuleMessage("You Must Provide An Owner, Module Name, And Template", MessageType.Warning);
AddModuleMessage(Localizer["You Must Provide A Valid Owner Name And Module Name ( ie. No Punctuation Or Spaces And The Values Cannot Be The Same ) And Choose A Template"], MessageType.Warning);
}
}
catch (Exception ex)
@ -91,34 +151,45 @@
}
}
private async void TemplateChanged(ChangeEventArgs e)
private async Task ActivateModule()
{
try
{
_location = string.Empty;
_template = (string)e.Value;
if (_template != "-")
if (!string.IsNullOrEmpty(_moduledefinitionname))
{
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";
}
}
Module module = await ModuleService.GetModuleAsync(ModuleState.ModuleId);
module.ModuleDefinitionName = _moduledefinitionname;
await ModuleService.UpdateModuleAsync(module);
NavigationManager.NavigateTo(NavigateUrl(), true);
}
StateHasChanged();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Getting System Info {Error}", ex.Message);
AddModuleMessage("Error Getting System Info", MessageType.Error);
await logger.LogError(ex, "Error Activating Module");
}
}
private bool IsValid(string name)
{
// must contain letters, underscores and digits and first character must be letter or underscore
return !string.IsNullOrEmpty(name) && Regex.IsMatch(name, "^[A-Za-z_][A-Za-z0-9_]*$");
}
private void TemplateChanged(ChangeEventArgs e)
{
_template = (string)e.Value;
GetLocation();
}
private void GetLocation()
{
_location = string.Empty;
if (_template != "-" && _systeminfo != null && _systeminfo.ContainsKey("serverpath"))
{
string[] path = _systeminfo["serverpath"].Split(Path.DirectorySeparatorChar);
_location = string.Join(Path.DirectorySeparatorChar, path, 0, path.Length - 2) +
Path.DirectorySeparatorChar + _owner + "." + _module;
}
StateHasChanged();
}
}

View File

@ -1,38 +1,39 @@
@namespace Oqtane.Modules.Admin.ModuleDefinitions
@namespace Oqtane.Modules.Admin.ModuleDefinitions
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IFileService FileService
@inject IModuleDefinitionService ModuleDefinitionService
@inject IPackageService PackageService
@inject IStringLocalizer<Add> Localizer
@if (_packages != null)
{
<TabStrip>
@if (_packages.Count > 0)
{
<TabPanel Name="Download">
<TabPanel Name="Download" ResourceKey="Download">
<ModuleMessage Type="MessageType.Info" Message="Download one or more modules from the list below. Once you are ready click Install to complete the installation."></ModuleMessage>
<Pager Items="@_packages">
<Header>
<th>Name</th>
<th>Version</th>
<th>@Localizer["Name"]</th>
<th>@Localizer["Version"]</th>
<th style="width: 1px"></th>
</Header>
<Row>
<td>@context.Name</td>
<td>@context.Version</td>
<td>
<button type="button" class="btn btn-primary" @onclick=@(async () => await DownloadModule(context.PackageId, context.Version))>Download</button>
<button type="button" class="btn btn-primary" @onclick=@(async () => await DownloadModule(context.PackageId, context.Version))>@Localizer["Download"]</button>
</td>
</Row>
</Pager>
</TabPanel>
}
<TabPanel Name="Upload">
<TabPanel Name="Upload" ResourceKey="Upload">
<table class="table table-borderless">
<tr>
<td>
<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." ResourceKey="Module">Module: </Label>
</td>
<td>
<FileManager Filter="nupkg" ShowFiles="false" Folder="Modules" UploadMultiple="true" />
@ -42,8 +43,8 @@
</TabPanel>
</TabStrip>
<button type="button" class="btn btn-success" @onclick="InstallModules">Install</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink>
<button type="button" class="btn btn-success" @onclick="InstallModules">@Localizer["Install"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@Localizer["Cancel"]</NavLink>
}
@code {
@ -69,7 +70,7 @@
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading Packages {Error}", ex.Message);
AddModuleMessage("Error Loading Packages", MessageType.Error);
AddModuleMessage(Localizer["Error Loading Packages"], MessageType.Error);
}
}
@ -77,14 +78,12 @@
{
try
{
ShowProgressIndicator();
var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 3);
await ModuleDefinitionService.InstallModuleDefinitionsAsync();
AddModuleMessage(Localizer["Module Installed Successfully. You Must <a href=\"{0}\">Restart</a> Your Application To Apply These Changes.", NavigateUrl("admin/system")], MessageType.Success);
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Installating Module");
await logger.LogError(ex, "Error Installing Module");
}
}
@ -94,13 +93,13 @@
{
await PackageService.DownloadPackageAsync(packageid, version, "Modules");
await logger.LogInformation("Module {ModuleDefinitionName} {Version} Downloaded Successfully", packageid, version);
AddModuleMessage("Modules Downloaded Successfully. Click Install To Complete Installation.", MessageType.Success);
AddModuleMessage(Localizer["Modules Downloaded Successfully. Click Install To Complete Installation."], MessageType.Success);
StateHasChanged();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Downloading Module {ModuleDefinitionName} {Version}", packageid, version);
AddModuleMessage("Error Downloading Module", MessageType.Error);
AddModuleMessage(Localizer["Error Downloading Module"], MessageType.Error);
}
}
}

View File

@ -0,0 +1,158 @@
@namespace Oqtane.Modules.Admin.ModuleDefinitions
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IModuleDefinitionService ModuleDefinitionService
@inject IModuleService ModuleService
@inject ISystemService SystemService
@inject ISettingService SettingService
@inject IStringLocalizer<Index> Localizer
@using System.Text.RegularExpressions
@using System.IO;
@if (_systeminfo != null && _templates != null)
{
<table class="table table-borderless">
<tr>
<td>
<Label For="owner" HelpText="Enter the name of the organization who is developing this module. It should not contain spaces or punctuation." ResourceKey="OwnerName">Owner Name: </Label>
</td>
<td>
<input id="owner" class="form-control" @bind="@_owner" />
</td>
</tr>
<tr>
<td>
<Label For="module" HelpText="Enter a name for this module. It should not contain spaces or punctuation." ResourceKey="ModuleName">Module Name: </Label>
</td>
<td>
<input id="module" class="form-control" @bind="@_module" />
</td>
</tr>
<tr>
<td>
<Label For="description" HelpText="Enter a short description for the module" ResourceKey="Description">Description: </Label>
</td>
<td>
<textarea id="description" class="form-control" @bind="@_description" rows="3"></textarea>
</td>
</tr>
<tr>
<td>
<Label For="template" HelpText="Select a module template. Templates are located in the wwwroot/Modules/Templates folder on the server." ResourceKey="Template">Template: </Label>
</td>
<td>
<select id="template" class="form-control" @onchange="(e => TemplateChanged(e))">
<option value="-">&lt;@Localizer["Select Template"]&gt;</option>
@foreach (string template in _templates)
{
<option value="@template">@template</option>
}
</select>
</td>
</tr>
<tr>
<td>
<Label For="reference" HelpText="Select a framework reference version" ResourceKey="FrameworkReference">Framework Reference: </Label>
</td>
<td>
<select id="reference" class="form-control" @bind="@_reference">
@foreach (string version in Constants.ReleaseVersions.Split(','))
{
if (Version.Parse(version).CompareTo(Version.Parse("2.0.0")) >= 0)
{
<option value="@(version)">@(version)</option>
}
}
<option value="local">@Localizer["Local Version"]</option>
</select>
</td>
</tr>
@if (!string.IsNullOrEmpty(_location))
{
<tr>
<td>
<Label For="location" HelpText="Location where the module will be created" ResourceKey="Location">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">@Localizer["Create Module"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@Localizer["Cancel"]</NavLink>
}
@code {
private string _owner = string.Empty;
private string _module = string.Empty;
private string _description = string.Empty;
private string _template = "-";
private string _reference = Constants.Version;
private string _location = string.Empty;
private Dictionary<string, string> _systeminfo;
private List<string> _templates;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
protected override async Task OnParametersSetAsync()
{
try
{
_systeminfo = await SystemService.GetSystemInfoAsync();
_templates = await ModuleDefinitionService.GetModuleDefinitionTemplatesAsync();
AddModuleMessage(Localizer["Please Note That The Module Creator Is Only Intended To Be Used In A Development Environment"], MessageType.Info);
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading Module Creator");
}
}
private async Task CreateModule()
{
try
{
if (IsValid(_owner) && IsValid(_module) && _owner != _module && _template != "-")
{
var moduleDefinition = new ModuleDefinition { Owner = _owner, Name = _module, Description = _description, Template = _template, Version = _reference };
moduleDefinition = await ModuleDefinitionService.CreateModuleDefinitionAsync(moduleDefinition);
GetLocation();
AddModuleMessage(Localizer["The Source Code For Your Module Has Been Created At The Location Specified Below And Must Be Compiled In Order To Make It Functional. Once It Has Been Compiled You Must <a href=\"{0}\">Restart</a> Your Application To Activate The Module.", NavigateUrl("admin/system")], MessageType.Success);
}
else
{
AddModuleMessage(Localizer["You Must Provide A Valid Owner Name And Module Name ( ie. No Punctuation Or Spaces And The Values Cannot Be The Same ) And Choose A Template"], MessageType.Warning);
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Creating Module");
}
}
private bool IsValid(string name)
{
// must contain letters, underscores and digits and first character must be letter or underscore
return !string.IsNullOrEmpty(name) && Regex.IsMatch(name, "^[A-Za-z_][A-Za-z0-9_]*$");
}
private void TemplateChanged(ChangeEventArgs e)
{
_template = (string)e.Value;
GetLocation();
}
private void GetLocation()
{
_location = string.Empty;
if (_template != "-" && _systeminfo != null && _systeminfo.ContainsKey("serverpath"))
{
string[] path = _systeminfo["serverpath"].Split(Path.DirectorySeparatorChar);
_location = string.Join(Path.DirectorySeparatorChar, path, 0, path.Length - 2) +
Path.DirectorySeparatorChar + _owner + "." + _module;
}
StateHasChanged();
}
}

View File

@ -1,14 +1,15 @@
@namespace Oqtane.Modules.Admin.ModuleDefinitions
@namespace Oqtane.Modules.Admin.ModuleDefinitions
@inherits ModuleBase
@inject IModuleDefinitionService ModuleDefinitionService
@inject NavigationManager NavigationManager
@inject IStringLocalizer<Edit> Localizer
<TabStrip>
<TabPanel Name="Definition">
<TabPanel Name="Definition" ResourceKey="Definition">
<table class="table table-borderless">
<tr>
<td>
<Label For="name" HelpText="The name of the module">Name: </Label>
<Label For="name" HelpText="The name of the module" ResourceKey="Name">Name: </Label>
</td>
<td>
<input id="name" class="form-control" @bind="@_name" />
@ -16,7 +17,7 @@
</tr>
<tr>
<td>
<Label For="description" HelpText="The description of the module">Description: </Label>
<Label For="description" HelpText="The description of the module" ResourceKey="Description">Description: </Label>
</td>
<td>
<textarea id="description" class="form-control" @bind="@_description" rows="2"></textarea>
@ -24,18 +25,18 @@
</tr>
<tr>
<td>
<Label For="categories" HelpText="Comma delimited list of module categories">Categories: </Label>
<Label For="categories" HelpText="Comma delimited list of module categories" ResourceKey="Categories">Categories: </Label>
</td>
<td>
<input id="categories" class="form-control" @bind="@_categories" />
</td>
</tr>
</table>
<Section Name="Information">
<Section Name="Information" ResourceKey="Information">
<table class="table table-borderless">
<tr>
<td>
<Label For="moduledefinitionname" HelpText="The internal name of the module">Internal Name: </Label>
<Label For="moduledefinitionname" HelpText="The internal name of the module" ResourceKey="InternalName">Internal Name: </Label>
</td>
<td>
<input id="moduledefinitionname" class="form-control" @bind="@_moduledefinitionname" disabled />
@ -43,7 +44,7 @@
</tr>
<tr>
<td>
<Label For="version" HelpText="The version of the module">Version: </Label>
<Label For="version" HelpText="The version of the module" ResourceKey="Version">Version: </Label>
</td>
<td>
<input id="version" class="form-control" @bind="@_version" disabled />
@ -51,7 +52,7 @@
</tr>
<tr>
<td>
<Label For="owner" HelpText="The owner or creator of the module">Owner: </Label>
<Label For="owner" HelpText="The owner or creator of the module" ResourceKey="Owner">Owner: </Label>
</td>
<td>
<input id="owner" class="form-control" @bind="@_owner" disabled />
@ -59,7 +60,7 @@
</tr>
<tr>
<td>
<Label For="url" HelpText="The reference url of the module">Reference Url: </Label>
<Label For="url" HelpText="The reference url of the module" ResourceKey="ReferenceUrl">Reference Url: </Label>
</td>
<td>
<input id="url" class="form-control" @bind="@_url" disabled />
@ -67,7 +68,7 @@
</tr>
<tr>
<td>
<Label For="contact" HelpText="The contact for the module">Contact: </Label>
<Label For="contact" HelpText="The contact for the module" ResourceKey="Contact">Contact: </Label>
</td>
<td>
<input id="contact" class="form-control" @bind="@_contact" disabled />
@ -75,16 +76,24 @@
</tr>
<tr>
<td>
<Label For="license" HelpText="The license of the module">License: </Label>
<Label For="license" HelpText="The module license terms" ResourceKey="License">License: </Label>
</td>
<td>
<textarea id="license" class="form-control" @bind="@_license" rows="5" disabled></textarea>
</td>
</tr>
<tr>
<td>
<Label For="runtimes" HelpText="The Blazor runtimes which this module supports" ResourceKey="Runtimes">Runtimes: </Label>
</td>
<td>
<input id="runtimes" class="form-control" @bind="@_runtimes" disabled />
</td>
</tr>
</table>
</Section>
</TabPanel>
<TabPanel Name="Permissions">
<TabPanel Name="Permissions" ResourceKey="Permissions">
<table class="table table-borderless">
<tr>
<td>
@ -94,9 +103,10 @@
</table>
</TabPanel>
</TabStrip>
<button type="button" class="btn btn-success" @onclick="SaveModuleDefinition">Save</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink>
<br /><br />
<button type="button" class="btn btn-success" @onclick="SaveModuleDefinition">@Localizer["Save"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@Localizer["Cancel"]</NavLink>
<br />
<br />
<AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon"></AuditInfo>
@code {
@ -110,6 +120,7 @@
private string _url = "";
private string _contact = "";
private string _license = "";
private string _runtimes = "";
private string _permissions;
private string _createdby;
private DateTime _createdon;
@ -139,6 +150,7 @@
_url = moduleDefinition.Url;
_contact = moduleDefinition.Contact;
_license = moduleDefinition.License;
_runtimes = moduleDefinition.Runtimes;
_permissions = moduleDefinition.Permissions;
_createdby = moduleDefinition.CreatedBy;
_createdon = moduleDefinition.CreatedOn;
@ -149,7 +161,7 @@
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading ModuleDefinition {ModuleDefinitionId} {Error}", _moduleDefinitionId, ex.Message);
AddModuleMessage("Error Loading Module", MessageType.Error);
AddModuleMessage(Localizer["Error Loading Module"], MessageType.Error);
}
}
@ -178,7 +190,7 @@
catch (Exception ex)
{
await logger.LogError(ex, "Error Saving ModuleDefinition {ModuleDefinitionId} {Error}", _moduleDefinitionId, ex.Message);
AddModuleMessage("Error Saving Module", MessageType.Error);
AddModuleMessage(Localizer["Error Saving Module"], MessageType.Error);
}
}
}

View File

@ -1,31 +1,34 @@
@namespace Oqtane.Modules.Admin.ModuleDefinitions
@namespace Oqtane.Modules.Admin.ModuleDefinitions
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IModuleDefinitionService ModuleDefinitionService
@inject IPackageService PackageService
@inject IStringLocalizer<Index> Localizer
@if (_moduleDefinitions == null)
{
<p><em>Loading...</em></p>
<p><em>@Localizer["Loading..."]</em></p>
}
else
{
<ActionLink Action="Add" Text="Install Module" />
<ActionLink Action="Add" Text="Install Module" ResourceKey="InstallModule" />
@((MarkupString)"&nbsp;")
<ActionLink Action="Create" Text="Create Module" ResourceKey="CreateModule" />
<Pager Items="@_moduleDefinitions">
<Header>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>Name</th>
<th>Version</th>
<th>@Localizer["Name"]</th>
<th>@Localizer["Version"]</th>
<th style="width: 1px;">&nbsp;</th>
</Header>
<Row>
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.ModuleDefinitionId.ToString())" /></td>
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.ModuleDefinitionId.ToString())" ResourceKey="EditModule" /></td>
<td>
@if (context.AssemblyName != "Oqtane.Client")
{
<ActionDialog Header="Delete Module" Message="@("Are You Sure You Wish To Delete The " + context.Name + " Module?")" Action="Delete" Security="SecurityAccessLevel.Host" Class="btn btn-danger" OnClick="@(async () => await DeleteModule(context))" />
<ActionDialog Header="Delete Module" Message="@Localizer["Are You Sure You Wish To Delete The {0} Module?", context.Name]" Action="Delete" Security="SecurityAccessLevel.Host" Class="btn btn-danger" OnClick="@(async () => await DeleteModule(context))" ResourceKey="DeleteModule" />
}
</td>
<td>@context.Name</td>
@ -33,7 +36,7 @@ else
<td>
@if (UpgradeAvailable(context.ModuleDefinitionName, context.Version))
{
<button type="button" class="btn btn-success" @onclick=@(async () => await DownloadModule(context.ModuleDefinitionName, context.Version))>Upgrade</button>
<button type="button" class="btn btn-success" @onclick=@(async () => await DownloadModule(context.ModuleDefinitionName, context.Version))>@Localizer["Upgrade"]</button>
}
</td>
</Row>
@ -46,7 +49,7 @@ else
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
protected override async Task OnInitializedAsync()
protected override async Task OnParametersSetAsync()
{
try
{
@ -58,7 +61,7 @@ else
if (_moduleDefinitions == null)
{
await logger.LogError(ex, "Error Loading Modules {Error}", ex.Message);
AddModuleMessage("Error Loading Modules", MessageType.Error);
AddModuleMessage(Localizer["Error Loading Modules"], MessageType.Error);
}
}
}
@ -84,15 +87,13 @@ else
{
await PackageService.DownloadPackageAsync(moduledefinitionname, version, "Modules");
await logger.LogInformation("Module Downloaded {ModuleDefinitionName} {Version}", moduledefinitionname, version);
ShowProgressIndicator();
var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 3);
await ModuleDefinitionService.InstallModuleDefinitionsAsync();
AddModuleMessage(Localizer["Module Installed Successfully. You Must <a href=\"{0}\">Restart</a> Your Application To Apply These Changes.", NavigateUrl("admin/system")], MessageType.Success);
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Downloading Module {ModuleDefinitionName} {Version} {Error}", moduledefinitionname, version, ex.Message);
AddModuleMessage("Error Downloading Module", MessageType.Error);
AddModuleMessage(Localizer["Error Downloading Module"], MessageType.Error);
}
}
@ -100,15 +101,14 @@ else
{
try
{
ShowProgressIndicator();
var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 3);
await ModuleDefinitionService.DeleteModuleDefinitionAsync(moduleDefinition.ModuleDefinitionId, moduleDefinition.SiteId);
AddModuleMessage(Localizer["Module Deleted Successfully"], MessageType.Success);
StateHasChanged();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Deleting Module {ModuleDefinition} {Error}", moduleDefinition, ex.Message);
AddModuleMessage("Error Deleting Module", MessageType.Error);
AddModuleMessage(Localizer["Error Deleting Module"], MessageType.Error);
}
}
}

View File

@ -1,13 +1,14 @@
@namespace Oqtane.Modules.Admin.Modules
@namespace Oqtane.Modules.Admin.Modules
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IModuleService ModuleService
@inject IStringLocalizer<Export> Localizer
<table class="table table-borderless">
<tbody>
<tr>
<td>
<Label For="content" HelpText="Enter the module content">Content: </Label>
<Label For="content" HelpText="Enter the module content" ResourceKey="Content">Content: </Label>
</td>
<td>
<textarea id="content" class="form-control" @bind="@_content" rows="5"></textarea>
@ -15,15 +16,15 @@
</tr>
</tbody>
</table>
<button type="button" class="btn btn-success" @onclick="ExportModule">Export</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink>
<button type="button" class="btn btn-success" @onclick="ExportModule">@Localizer["Export"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@Localizer["Cancel"]</NavLink>
@code {
private string _content = string.Empty;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
public override string Title => "Export Module";
public override string Title => "Export Content";
private async Task ExportModule()

View File

@ -1,13 +1,14 @@
@namespace Oqtane.Modules.Admin.Modules
@namespace Oqtane.Modules.Admin.Modules
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IModuleService ModuleService
@inject IStringLocalizer<Import> Localizer
<table class="table table-borderless">
<tbody>
<tr>
<td>
<Label For="content" HelpText="Enter the module content">Content: </Label>
<Label For="content" HelpText="Enter the module content" ResourceKey="Content">Content: </Label>
</td>
<td>
<textarea id="content" class="form-control" @bind="@_content" rows="5"></textarea>
@ -15,15 +16,15 @@
</tr>
</tbody>
</table>
<button type="button" class="btn btn-success" @onclick="ImportModule">Import</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink>
<button type="button" class="btn btn-success" @onclick="ImportModule">@Localizer["Import"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@Localizer["Cancel"]</NavLink>
@code {
private string _content = string.Empty;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
public override string Title => "Import Module";
public override string Title => "Import Content";
private async Task ImportModule()
{
@ -31,19 +32,25 @@
{
try
{
await ModuleService.ImportModuleAsync(ModuleState.ModuleId, _content);
StateHasChanged();
NavigationManager.NavigateTo(NavigateUrl());
bool success = await ModuleService.ImportModuleAsync(ModuleState.ModuleId, _content);
if (success)
{
AddModuleMessage(Localizer["Content Imported Successfully"], MessageType.Success);
}
else
{
AddModuleMessage(Localizer["A Problem Was Encountered Importing Content. Please Ensure The Content Is Formatted Correctly For The Module."], MessageType.Warning);
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Importing Module {ModuleId} {Error}", ModuleState.ModuleId, ex.Message);
AddModuleMessage("Error Importing Module", MessageType.Error);
AddModuleMessage(Localizer["Error Importing Module"], MessageType.Error);
}
}
else
{
AddModuleMessage("You Must Enter Some Content To Import", MessageType.Warning);
AddModuleMessage(Localizer["You Must Enter Some Content To Import"], MessageType.Warning);
}
}
}

View File

@ -1,18 +1,20 @@
@namespace Oqtane.Modules.Admin.Modules
@namespace Oqtane.Modules.Admin.Modules
@using Oqtane.Interfaces
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IThemeService ThemeService
@inject IModuleService ModuleService
@inject IPageModuleService PageModuleService
@inject IStringLocalizer<Settings> Localizer
<TabStrip>
<TabPanel Name="Settings" Heading="Module Settings">
<TabPanel Name="Settings" Heading="Settings" ResourceKey="Settings">
@if (_containers != null)
{
<table class="table table-borderless">
<tr>
<td>
<Label For="title" HelpText="Enter the title of the module">Title: </Label>
<Label For="title" HelpText="Enter the title of the module" ResourceKey="Title">Title: </Label>
</td>
<td>
<input id="title" type="text" name="Title" class="form-control" @bind="@_title" />
@ -20,11 +22,10 @@
</tr>
<tr>
<td>
<Label For="container" HelpText="Select the module's container">Container: </Label>
<Label For="container" HelpText="Select the module's container" ResourceKey="Container">Container: </Label>
</td>
<td>
<select id="container" class="form-control" @bind="@_containerType">
<option value="-">&lt;Inherit From Page Or Site&gt;</option>
@foreach (var container in _containers)
{
<option value="@container.TypeName">@container.Name</option>
@ -34,18 +35,18 @@
</tr>
<tr>
<td>
<Label For="allpages" HelpText="Indicate if this module should be displayed on all pages">Display On All Pages? </Label>
<Label For="allpages" HelpText="Indicate if this module should be displayed on all pages" ResourceKey="DisplayOnAllPages">Display On All Pages? </Label>
</td>
<td>
<select id="allpages" class="form-control" @bind="@_allPages">
<option value="True">Yes</option>
<option value="False">No</option>
<option value="True">@Localizer["Yes"]</option>
<option value="False">@Localizer["No"]</option>
</select>
</td>
</tr>
<tr>
<td>
<Label For="page" HelpText="The page that the module is on">Page: </Label>
<Label For="page" HelpText="The page that the module is located on" ResourceKey="Page">Page: </Label>
</td>
<td>
<select id="page" class="form-control" @bind="@_pageId">
@ -62,7 +63,7 @@
</table>
}
</TabPanel>
<TabPanel Name="Permissions">
<TabPanel Name="Permissions" ResourceKey="Permissions">
@if (_permissions != null)
{
<table class="table table-borderless">
@ -74,17 +75,27 @@
</table>
}
</TabPanel>
@if (_settingsModuleType != null)
@if (_moduleSettingsType != null)
{
<TabPanel Name="ModuleSettings" Heading="@_settingstitle">
@DynamicComponent
<TabPanel Name="ModuleSettings" Heading="@_moduleSettingsTitle" ResourceKey="ModuleSettings">
@ModuleSettingsComponent
</TabPanel>
}
@if (_containerSettingsType != null)
{
<TabPanel Name="ContainerSettings" Heading="Container Settings" ResourceKey="ContainerSettings">
@ContainerSettingsComponent
</TabPanel>
}
</TabStrip>
<button type="button" class="btn btn-success" @onclick="SaveModule">Save</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink>
<button type="button" class="btn btn-success" @onclick="SaveModule">@Localizer["Save"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@Localizer["Cancel"]</NavLink>
@code {
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
public override string Title => "Module Settings";
private List<Theme> _themes;
private List<ThemeControl> _containers = new List<ThemeControl>();
private string _title;
private string _containerType;
@ -93,50 +104,65 @@
private string _permissions = null;
private string _pageId;
private PermissionGrid _permissionGrid;
private Type _settingsModuleType;
private string _settingstitle = "Other Settings";
private object _settings;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
public override string Title => "Module Settings";
private RenderFragment DynamicComponent { get; set; }
private Type _moduleSettingsType;
private object _moduleSettings;
private string _moduleSettingsTitle = "Module Settings";
private RenderFragment ModuleSettingsComponent { get; set; }
private Type _containerSettingsType;
private object _containerSettings;
private RenderFragment ContainerSettingsComponent { get; set; }
protected override async Task OnInitializedAsync()
{
_title = ModuleState.Title;
_containers = ThemeService.GetContainerControls(await ThemeService.GetThemesAsync(), PageState.Page.ThemeType);
_themes = await ThemeService.GetThemesAsync();
_containers = ThemeService.GetContainerControls(_themes, PageState.Page.ThemeType);
_containerType = ModuleState.ContainerType;
if (!string.IsNullOrEmpty(PageState.Page.DefaultContainerType) && _containerType == PageState.Page.DefaultContainerType)
{
_containerType = "-";
}
if (_containerType == PageState.Site.DefaultContainerType)
{
_containerType = "-";
}
_allPages = ModuleState.AllPages.ToString();
_permissions = ModuleState.Permissions;
_permissionNames = ModuleState.ModuleDefinition.PermissionNames;
_pageId = ModuleState.PageId.ToString();
_settingsModuleType = Type.GetType(ModuleState.ModuleType);
if (_settingsModuleType != null)
if (!string.IsNullOrEmpty(ModuleState.ModuleDefinition.SettingsType))
{
var moduleobject = Activator.CreateInstance(_settingsModuleType) as IModuleControl;
_settingstitle = moduleobject.Title;
if (string.IsNullOrEmpty(_settingstitle))
// module settings type explicitly declared in IModule interface
_moduleSettingsType = Type.GetType(ModuleState.ModuleDefinition.SettingsType);
}
else
{
// legacy support - module settings type determined by convention ( ie. existence of a "Settings.razor" component in module )
_moduleSettingsType = Type.GetType(ModuleState.ModuleDefinition.ControlTypeTemplate.Replace(Constants.ActionToken, PageState.Action), false, true);
}
if (_moduleSettingsType != null)
{
var moduleobject = Activator.CreateInstance(_moduleSettingsType) as IModuleControl;
if (!string.IsNullOrEmpty(moduleobject.Title))
{
_settingstitle = "Other Settings";
_moduleSettingsTitle = moduleobject.Title;
}
DynamicComponent = builder =>
ModuleSettingsComponent = builder =>
{
builder.OpenComponent(0, _settingsModuleType);
builder.AddComponentReferenceCapture(1, inst => { _settings = Convert.ChangeType(inst, _settingsModuleType); });
builder.OpenComponent(0, _moduleSettingsType);
builder.AddComponentReferenceCapture(1, inst => { _moduleSettings = Convert.ChangeType(inst, _moduleSettingsType); });
builder.CloseComponent();
};
}
var theme = _themes.FirstOrDefault(item => item.Containers.Any(themecontrol => themecontrol.TypeName.Equals(_containerType)));
if (theme != null && !string.IsNullOrEmpty(theme.ContainerSettingsType))
{
_containerSettingsType = Type.GetType(theme.ContainerSettingsType);
if (_containerSettingsType != null)
{
ContainerSettingsComponent = builder =>
{
builder.OpenComponent(0, _containerSettingsType);
builder.AddComponentReferenceCapture(1, inst => { _containerSettings = Convert.ChangeType(inst, _containerSettingsType); });
builder.CloseComponent();
};
}
}
}
private async Task SaveModule()
@ -161,13 +187,23 @@
module.Permissions = _permissionGrid.GetPermissions();
await ModuleService.UpdateModuleAsync(module);
if (_settingsModuleType != null)
if (_moduleSettingsType != null)
{
var moduleType = Type.GetType(ModuleState.ModuleType);
if (moduleType != null)
if (_moduleSettings is ISettingsControl moduleSettingsControl)
{
moduleType.GetMethod("UpdateSettings")?.Invoke(_settings, null); // method must be public in settings component
// module settings updated using explicit interface
await moduleSettingsControl.UpdateSettings();
}
else
{
// legacy support - module settings updated by convention ( ie. by calling a public method named "UpdateSettings" in settings component )
_moduleSettings?.GetType().GetMethod("UpdateSettings")?.Invoke(_moduleSettings, null);
}
}
if (_containerSettingsType != null && _containerSettings is ISettingsControl containerSettingsControl)
{
await containerSettingsControl.UpdateSettings();
}
NavigationManager.NavigateTo(NavigateUrl());

View File

@ -1,17 +1,18 @@
@namespace Oqtane.Modules.Admin.Pages
@namespace Oqtane.Modules.Admin.Pages
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IPageService PageService
@inject IThemeService ThemeService
@inject IStringLocalizer<Add> Localizer
<TabStrip>
<TabPanel Name="Settings">
<TabPanel Name="Settings" ResourceKey="Settings">
@if (_themeList != null)
{
<table class="table table-borderless">
<tr>
<td>
<Label For="Name" HelpText="Enter the page name">Name: </Label>
<Label For="Name" HelpText="Enter the page name" ResourceKey="Name">Name: </Label>
</td>
<td>
<input id="Name" class="form-control" @bind="@_name" />
@ -19,11 +20,11 @@
</tr>
<tr>
<td>
<Label For="Parent" HelpText="Select the parent for the page in the site hierarchy">Parent: </Label>
<Label For="Parent" HelpText="Select the parent for the page in the site hierarchy" ResourceKey="Parent">Parent: </Label>
</td>
<td>
<select id="Parent" class="form-control" @onchange="(e => ParentChanged(e))">
<option value="-1">&lt;Site Root&gt;</option>
<option value="-1">&lt;@Localizer["Site Root"]&gt;</option>
@foreach (Page page in _pageList)
{
<option value="@(page.PageId)">@(new string('-', page.Level * 2))@(page.Name)</option>
@ -33,22 +34,22 @@
</tr>
<tr>
<td>
<Label For="Insert" HelpText="Select the location where you would like the page to be inserted in relation to other pages">Insert: </Label>
<Label For="Insert" HelpText="Select the location where you would like the page to be inserted in relation to other pages" ResourceKey="Insert">Insert: </Label>
</td>
<td>
<select id="Insert" class="form-control" @bind="@_insert">
<option value="<<">At Beginning</option>
<option value="<<">@Localizer["At Beginning"]</option>
@if (_children != null && _children.Count > 0)
{
<option value="<">Before</option>
<option value=">">After</option>
<option value="<">@Localizer["Before"]</option>
<option value=">">@Localizer["After"]</option>
}
<option value=">>">At End</option>
<option value=">>">@Localizer["At End"]</option>
</select>
@if (_children != null && _children.Count > 0 && (_insert == "<" || _insert == ">"))
{
<select class="form-control" @bind="@_childid">
<option value="-1">&lt;Select Page&gt;</option>
<option value="-1">&lt;@Localizer["Select Page"]&gt;</option>
@foreach (Page page in _children)
{
<option value="@(page.PageId)">@(page.Name)</option>
@ -59,18 +60,18 @@
</tr>
<tr>
<td>
<Label For="Navigation" HelpText="Select whether the page is part of the site navigation or hidden">Navigation? </Label>
<Label For="Navigation" HelpText="Select whether the page is part of the site navigation or hidden" ResourceKey="Navigation">Navigation? </Label>
</td>
<td>
<select id="Navigation" class="form-control" @bind="@_isnavigation">
<option value="True">Yes</option>
<option value="False">No</option>
<option value="True">@Localizer["Yes"]</option>
<option value="False">@Localizer["No"]</option>
</select>
</td>
</tr>
<tr>
<td>
<Label For="Path" HelpText="Optionally enter a url path for this page (ie. home ). If you do not provide a url path, the page name will be used.">Url Path: </Label>
<Label For="Path" HelpText="Optionally enter a url path for this page (ie. home ). If you do not provide a url path, the page name will be used." ResourceKey="UrlPath">Url Path: </Label>
</td>
<td>
<input id="Path" class="form-control" @bind="@_path" />
@ -78,18 +79,18 @@
</tr>
<tr>
<td>
<Label For="Url" HelpText="Optionally enter a url which this page should redirect to when a user navigates to it">Redirect: </Label>
<Label For="Url" HelpText="Optionally enter a url which this page should redirect to when a user navigates to it" ResourceKey="Redirect">Redirect: </Label>
</td>
<td>
<input id="Url" class="form-control" @bind="@_url" />
</td>
</tr>
</table>
<Section Name="Appearance">
<Section Name="Appearance" ResourceKey="Appearance">
<table class="table table-borderless">
<tr>
<td>
<Label For="Title" HelpText="Optionally enter the page title. If you do not provide a page title, the page name will be used.">Title: </Label>
<Label For="Title" HelpText="Optionally enter the page title. If you do not provide a page title, the page name will be used." ResourceKey="Title">Title: </Label>
</td>
<td>
<input id="Title" class="form-control" @bind="@_title" />
@ -97,56 +98,24 @@
</tr>
<tr>
<td>
<Label For="Theme" HelpText="Select the theme for this page">Theme: </Label>
<Label For="Theme" HelpText="Select the theme for this page" ResourceKey="Theme">Theme: </Label>
</td>
<td>
<select id="Theme" class="form-control" @onchange="(e => ThemeChanged(e))">
<option value="-">&lt;Inherit From Site&gt;</option>
<select id="Theme" class="form-control" value="@_themetype" @onchange="(e => ThemeChanged(e))">
@foreach (var theme in _themes)
{
if (theme.TypeName == _themetype)
{
<option value="@theme.TypeName" selected>@theme.Name</option>
}
else
{
<option value="@theme.TypeName">@theme.Name</option>
}
<option value="@theme.TypeName">@theme.Name</option>
}
</select>
</td>
</tr>
@if (_layouts.Count > 0)
{
<tr>
<td>
<Label For="Layout" HelpText="Select a layout for the page (if the selected theme supports it)">Layout: </Label>
</td>
<td>
<select id="Layout" class="form-control" @bind="@_layouttype">
<option value="-">&lt;Inherit From Site&gt;</option>
@foreach (var layout in _layouts)
{
if (layout.TypeName == _layouttype)
{
<option value="@(layout.TypeName)" selected>@(layout.Name)</option>
}
else
{
<option value="@(layout.TypeName)">@(layout.Name)</option>
}
}
</select>
</td>
</tr>
}
<tr>
<td>
<Label For="defaultContainer" HelpText="Select the default container for the page">Default Container: </Label>
<Label For="defaultContainer" HelpText="Select the default container for the page" ResourceKey="DefaultContainer">Default Container: </Label>
</td>
<td>
<select id="defaultContainer" class="form-control" @bind="@_containertype">
<option value="-">&lt;Inherit From Site&gt;</option>
<option value="-">&lt;@Localizer["Select Container"]&gt;</option>
@foreach (var container in _containers)
{
<option value="@container.TypeName">@container.Name</option>
@ -156,7 +125,7 @@
</tr>
<tr>
<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 class name for this page which will be displayed in the site navigation" ResourceKey="Icon">Icon: </Label>
</td>
<td>
<input id="Icon" class="form-control" @bind="@_icon" />
@ -164,23 +133,12 @@
</tr>
<tr>
<td>
<Label For="Default-Mode" HelpText="Select the default administration mode you want for this page">Default Mode? </Label>
</td>
<td>
<select id="Default-Mode" class="form-control" @bind="@_mode">
<option value="view">View Mode</option>
<option value="edit">Edit Mode</option>
</select>
</td>
</tr>
<tr>
<td>
<Label For="Personalizable" HelpText="Select whether you would like users to be able to personalize this page with their own content">Personalizable? </Label>
<Label For="Personalizable" HelpText="Select whether you would like users to be able to personalize this page with their own content" ResourceKey="Personalizable">Personalizable? </Label>
</td>
<td>
<select id="Personalizable" class="form-control" @bind="@_ispersonalizable">
<option value="True">Yes</option>
<option value="False">No</option>
<option value="True">@Localizer["Yes"]</option>
<option value="False">@Localizer["No"]</option>
</select>
</td>
</tr>
@ -188,7 +146,7 @@
</Section>
}
</TabPanel>
<TabPanel Name="Permissions">
<TabPanel Name="Permissions" ResourceKey="Permissions">
<table class="table table-borderless">
<tr>
<td>
@ -197,14 +155,21 @@
</tr>
</table>
</TabPanel>
@if (_themeSettingsType != null)
{
<TabPanel Name="ThemeSettings" Heading="Theme Settings" ResourceKey="ThemeSettings">
@ThemeSettingsComponent
</TabPanel>
}
</TabStrip>
<button type="button" class="btn btn-success" @onclick="SavePage">Save</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink>
<button type="button" class="btn btn-success" @onclick="SavePage">@Localizer["Save"]</button>
<button type="button" class="btn btn-secondary" @onclick="Cancel">@Localizer["Cancel"]</button>
@code {
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
private List<Theme> _themeList;
private List<ThemeControl> _themes = new List<ThemeControl>();
private List<ThemeControl> _layouts = new List<ThemeControl>();
private List<ThemeControl> _containers = new List<ThemeControl>();
private List<Page> _pageList;
private string _name;
@ -217,31 +182,33 @@
private string _isnavigation = "True";
private string _url;
private string _ispersonalizable = "False";
private string _mode = "view";
private string _themetype = "-";
private string _layouttype = "-";
private string _containertype = "-";
private string _themetype = string.Empty;
private string _containertype = string.Empty;
private string _icon = string.Empty;
private string _permissions = string.Empty;
private PermissionGrid _permissionGrid;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
private Type _themeSettingsType;
private object _themeSettings;
private RenderFragment ThemeSettingsComponent { get; set; }
protected override async Task OnInitializedAsync()
{
try
{
_themeList = await ThemeService.GetThemesAsync();
_themes = ThemeService.GetThemeControls(_themeList);
_themetype = PageState.Site.DefaultThemeType;
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
_containertype = PageState.Site.DefaultContainerType;
_pageList = PageState.Pages;
_children = PageState.Pages.Where(item => item.ParentId == null).ToList();
_themes = ThemeService.GetThemeControls(_themeList);
_permissions = string.Empty;
ThemeSettings();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Initializing Page {Error}", ex.Message);
AddModuleMessage("Error Initializing Page", MessageType.Error);
AddModuleMessage(Localizer["Error Initializing Page"], MessageType.Error);
}
}
@ -276,7 +243,7 @@
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading Child Pages For Parent {PageId} {Error}", _parentid, ex.Message);
AddModuleMessage("Error Loading Child Pages For Parent", MessageType.Error);
AddModuleMessage(Localizer["Error Loading Child Pages For Parent"], MessageType.Error);
}
}
@ -285,24 +252,34 @@
try
{
_themetype = (string)e.Value;
if (_themetype != "-")
{
_layouts = ThemeService.GetLayoutControls(_themeList, _themetype);
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
}
else
{
_layouts = new List<ThemeControl>();
_containers = new List<ThemeControl>();
}
_layouttype = "-";
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
_containertype = "-";
ThemeSettings();
StateHasChanged();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading Pane Layouts For Theme {ThemeType} {Error}", _themetype, ex.Message);
AddModuleMessage("Error Loading Pane Layouts For Theme", MessageType.Error);
AddModuleMessage(Localizer["Error Loading Pane Layouts For Theme"], MessageType.Error);
}
}
private void ThemeSettings()
{
_themeSettingsType = null;
var theme = _themeList.FirstOrDefault(item => item.Themes.Any(themecontrol => themecontrol.TypeName.Equals(_themetype)));
if (theme != null && !string.IsNullOrEmpty(theme.ThemeSettingsType))
{
_themeSettingsType = Type.GetType(theme.ThemeSettingsType);
if (_themeSettingsType != null)
{
ThemeSettingsComponent = builder =>
{
builder.OpenComponent(0, _themeSettingsType);
builder.AddComponentReferenceCapture(1, inst => { _themeSettings = Convert.ChangeType(inst, _themeSettingsType); });
builder.CloseComponent();
};
}
}
}
@ -311,7 +288,7 @@
Page page = null;
try
{
if (_name != string.Empty && !string.IsNullOrEmpty(_themetype) && (_layouts.Count == 0 || !string.IsNullOrEmpty(_layouttype)))
if (_name != string.Empty && !string.IsNullOrEmpty(_themetype) && _containertype != "-")
{
page = new Page();
page.SiteId = PageState.Page.SiteId;
@ -346,6 +323,12 @@
}
}
if (!PagePathIsUnique(page.Path, page.SiteId, _pageList))
{
AddModuleMessage(Localizer["A page with path {0} already exists for the selected parent page. The page path needs to be unique for the selected parent.", _path], MessageType.Warning);
return;
}
Page child;
switch (_insert)
{
@ -367,17 +350,11 @@
page.IsNavigation = (_isnavigation == null ? true : Boolean.Parse(_isnavigation));
page.Url = _url;
page.EditMode = (_mode == "edit" ? true : false);
page.ThemeType = (_themetype != "-") ? _themetype : string.Empty;
if (!string.IsNullOrEmpty(page.ThemeType) && page.ThemeType == PageState.Site.DefaultThemeType)
{
page.ThemeType = string.Empty;
}
page.LayoutType = (_layouttype != "-") ? _layouttype : string.Empty;
if (!string.IsNullOrEmpty(page.LayoutType) && page.LayoutType == PageState.Site.DefaultLayoutType)
{
page.LayoutType = string.Empty;
}
page.DefaultContainerType = (_containertype != "-") ? _containertype : string.Empty;
if (!string.IsNullOrEmpty(page.DefaultContainerType) && page.DefaultContainerType == PageState.Site.DefaultContainerType)
{
@ -392,19 +369,42 @@
await PageService.UpdatePageOrderAsync(page.SiteId, page.PageId, page.ParentId);
await logger.LogInformation("Page Added {Page}", page);
NavigationManager.NavigateTo(NavigateUrl(page.Path));
if (PageState.QueryString.ContainsKey("cp"))
{
NavigationManager.NavigateTo(NavigateUrl(PageState.Pages.First(item => item.PageId == int.Parse(PageState.QueryString["cp"])).Path));
}
else
{
NavigationManager.NavigateTo(NavigateUrl(page.Path));
}
}
else
{
AddModuleMessage("You Must Provide Page Name And Theme", MessageType.Warning);
AddModuleMessage(Localizer["You Must Provide Page Name, Theme, and Container"], MessageType.Warning);
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Saving Page {Page} {Error}", page, ex.Message);
AddModuleMessage("Error Saving Page", MessageType.Error);
AddModuleMessage(Localizer["Error Saving Page"], MessageType.Error);
}
}
private void Cancel()
{
if (PageState.QueryString.ContainsKey("cp"))
{
NavigationManager.NavigateTo(NavigateUrl(PageState.Pages.First(item => item.PageId == int.Parse(PageState.QueryString["cp"])).Path));
}
else
{
NavigationManager.NavigateTo(NavigateUrl());
}
}
private static bool PagePathIsUnique(string pagePath, int siteId, List<Page> existingPages)
{
return !existingPages.Any(page => page.SiteId == siteId && page.Path == pagePath);
}
}

View File

@ -1,17 +1,19 @@
@namespace Oqtane.Modules.Admin.Pages
@namespace Oqtane.Modules.Admin.Pages
@using Oqtane.Interfaces
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IPageService PageService
@inject IThemeService ThemeService
@inject IStringLocalizer<Edit> Localizer
<TabStrip>
<TabPanel Name="Settings">
<TabPanel Name="Settings" ResourceKey="Settings">
@if (_themeList != null)
{
<table class="table table-borderless">
<tr>
<td>
<Label For="Name" HelpText="Enter the page name">Name: </Label>
<Label For="Name" HelpText="Enter the page name" ResourceKey="Name">Name: </Label>
</td>
<td>
<input id="Name" class="form-control" @bind="@_name" />
@ -19,18 +21,14 @@
</tr>
<tr>
<td>
<Label For="Parent" HelpText="Select the parent for the page in the site hierarchy">Parent: </Label>
<Label For="Parent" HelpText="Select the parent for the page in the site hierarchy" ResourceKey="Parent">Parent: </Label>
</td>
<td>
<select id="Parent" class="form-control" @onchange="(e => ParentChanged(e))">
<option value="-1">&lt;Site Root&gt;</option>
<select id="Parent" class="form-control" value="@_parentid" @onchange="(e => ParentChanged(e))">
<option value="-1">&lt;@Localizer["Site Root"]&gt;</option>
@foreach (Page page in _pageList)
{
if (page.PageId.ToString() == _parentid)
{
<option value="@(page.PageId)" selected>@(new string('-', page.Level * 2))@(page.Name)</option>
}
else
if (page.PageId != _pageId)
{
<option value="@(page.PageId)">@(new string('-', page.Level * 2))@(page.Name)</option>
}
@ -40,26 +38,26 @@
</tr>
<tr>
<td>
<Label For="Move" HelpText="Select the location where you would like the page to be moved in relation to other pages">Move: </Label>
<Label For="Move" HelpText="Select the location where you would like the page to be moved in relation to other pages" ResourceKey="Move">Move: </Label>
</td>
<td>
<select id="Move" class="form-control" @bind="@_insert">
@if (_parentid == _currentparentid)
{
<option value="=">&lt;Maintain Current Location&gt;</option>
<option value="=">&lt;@Localizer["Maintain Current Location"]&gt;</option>
}
<option value="<<">To Beginning</option>
<option value="<<">@Localizer["To Beginning"]</option>
@if (_children != null && _children.Count > 0)
{
<option value="<">Before</option>
<option value=">">After</option>
<option value="<">@Localizer["Before"]</option>
<option value=">">@Localizer["After"]</option>
}
<option value=">>">To End</option>
<option value=">>">@Localizer["To End"]</option>
</select>
@if (_children != null && _children.Count > 0 && (_insert == "<" || _insert == ">"))
{
<select class="form-control" @bind="@_childid">
<option value="-1">&lt;Select Page&gt;</option>
<option value="-1">&lt;@Localizer["Select Page"]&gt;</option>
@foreach (Page page in _children)
{
<option value="@(page.PageId)">@(page.Name)</option>
@ -70,18 +68,18 @@
</tr>
<tr>
<td>
<Label For="Navigation" HelpText="Select whether the page is part of the site navigation or hidden">Navigation? </Label>
<Label For="Navigation" HelpText="Select whether the page is part of the site navigation or hidden" ResourceKey="Navigation">Navigation? </Label>
</td>
<td>
<select id="Navigation" class="form-control" @bind="@_isnavigation">
<option value="True">Yes</option>
<option value="False">No</option>
<option value="True">@Localizer["Yes"]</option>
<option value="False">@Localizer["No"]</option>
</select>
</td>
</tr>
<tr>
<td>
<Label For="Path" HelpText="Optionally enter a url path for this page (ie. home ). If you do not provide a url path, the page name will be used.">Url Path: </Label>
<Label For="Path" HelpText="Optionally enter a url path for this page (ie. home ). If you do not provide a url path, the page name will be used." ResourceKey="UrlPath">Url Path: </Label>
</td>
<td>
<input id="Path" class="form-control" @bind="@_path" />
@ -89,18 +87,18 @@
</tr>
<tr>
<td>
<Label For="Url" HelpText="Optionally enter a url which this page should redirect to when a user navigates to it">Redirect: </Label>
<Label For="Url" HelpText="Optionally enter a url which this page should redirect to when a user navigates to it" ResourceKey="Redirect">Redirect: </Label>
</td>
<td>
<input id="Url" class="form-control" @bind="@_url" />
</td>
</tr>
</table>
<Section Name="Appearance">
<Section Name="Appearance" ResourceKey="Appearance">
<table class="table table-borderless">
<tr>
<td>
<Label For="Title" HelpText="Optionally enter the page title. If you do not provide a page title, the page name will be used.">Title: </Label>
<Label For="Title" HelpText="Optionally enter the page title. If you do not provide a page title, the page name will be used." ResourceKey="Title">Title: </Label>
</td>
<td>
<input id="Title" class="form-control" @bind="@_title" />
@ -108,56 +106,24 @@
</tr>
<tr>
<td>
<Label For="Theme" HelpText="Select the theme for this page">Theme: </Label>
<Label For="Theme" HelpText="Select the theme for this page" ResourceKey="Theme">Theme: </Label>
</td>
<td>
<select id="Theme" class="form-control" @onchange="(e => ThemeChanged(e))">
<option value="-">&lt;Inherit From Site&gt;</option>
<select id="Theme" class="form-control" value="@_themetype" @onchange="(e => ThemeChanged(e))">
@foreach (var theme in _themes)
{
if (theme.TypeName == _themetype)
{
<option value="@theme.TypeName" selected>@theme.Name</option>
}
else
{
<option value="@theme.TypeName">@theme.Name</option>
}
<option value="@theme.TypeName">@theme.Name</option>
}
</select>
</td>
</tr>
@if (_layouts.Count > 0)
{
<tr>
<td>
<Label For="Layout" HelpText="Select a layout for the page (if the selected theme supports it)">Layout: </Label>
</td>
<td>
<select id="Layout" class="form-control" @bind="@_layouttype">
<option value="-">&lt;Inherit From Site&gt;</option>
@foreach (var layout in _layouts)
{
if (layout.TypeName == _layouttype)
{
<option value="@(layout.TypeName)" selected>@(layout.Name)</option>
}
else
{
<option value="@(layout.TypeName)">@(layout.Name)</option>
}
}
</select>
</td>
</tr>
}
<tr>
<td>
<Label For="defaultContainer" HelpText="Select the default container for the page">Default Container: </Label>
<Label For="defaultContainer" HelpText="Select the default container for the page" ResourceKey="DefaultContainer">Default Container: </Label>
</td>
<td>
<select id="defaultContainer" class="form-control" @bind="@_containertype">
<option value="-">&lt;Inherit From Site&gt;</option>
<option value="-">&lt;@Localizer["Select Container"]&gt;</option>
@foreach (var container in _containers)
{
<option value="@container.TypeName">@container.Name</option>
@ -167,7 +133,7 @@
</tr>
<tr>
<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 class name for this page which will be displayed in the site navigation" ResourceKey="Icon">Icon: </Label>
</td>
<td>
<input id="Icon" class="form-control" @bind="@_icon" />
@ -175,23 +141,12 @@
</tr>
<tr>
<td>
<Label For="Default-Mode" HelpText="Select the default administration mode you want for this page">Default Mode? </Label>
</td>
<td>
<select id="Default-Mode" class="form-control" @bind="@_mode">
<option value="view">View Mode</option>
<option value="edit">Edit Mode</option>
</select>
</td>
</tr>
<tr>
<td>
<Label For="Personalizable" HelpText="Select whether you would like users to be able to personalize this page with their own content">Personalizable? </Label>
<Label For="Personalizable" HelpText="Select whether you would like users to be able to personalize this page with their own content" ResourceKey="Personalizable">Personalizable? </Label>
</td>
<td>
<select id="Personalizable" class="form-control" @bind="@_ispersonalizable">
<option value="True">Yes</option>
<option value="False">No</option>
<option value="True">@Localizer["Yes"]</option>
<option value="False">@Localizer["No"]</option>
</select>
</td>
</tr>
@ -201,7 +156,7 @@
<AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon" DeletedBy="@_deletedby" DeletedOn="@_deletedon"></AuditInfo>
}
</TabPanel>
<TabPanel Name="Permissions">
<TabPanel Name="Permissions" ResourceKey="Permissions">
@if (_permissions != null)
{
<table class="table table-borderless">
@ -213,14 +168,21 @@
</table>
}
</TabPanel>
@if (_themeSettingsType != null)
{
<TabPanel Name="ThemeSettings" Heading="Theme Settings" ResourceKey="ThemeSettings">
@ThemeSettingsComponent
</TabPanel>
}
</TabStrip>
<button type="button" class="btn btn-success" @onclick="SavePage">Save</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink>
<button type="button" class="btn btn-success" @onclick="SavePage">@Localizer["Save"]</button>
<button type="button" class="btn btn-secondary" @onclick="Cancel">@Localizer["Cancel"]</button>
@code {
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
private List<Theme> _themeList;
private List<ThemeControl> _themes = new List<ThemeControl>();
private List<ThemeControl> _layouts = new List<ThemeControl>();
private List<ThemeControl> _containers = new List<ThemeControl>();
private List<Page> _pageList;
private int _pageId;
@ -235,9 +197,7 @@
private string _isnavigation;
private string _url;
private string _ispersonalizable;
private string _mode;
private string _themetype = "-";
private string _layouttype = "-";
private string _themetype;
private string _containertype = "-";
private string _icon;
private string _permissions = null;
@ -247,12 +207,10 @@
private DateTime _modifiedon;
private string _deletedby;
private DateTime? _deletedon;
#pragma warning disable 649
private PermissionGrid _permissionGrid;
#pragma warning restore 649
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
private Type _themeSettingsType;
private object _themeSettings;
private RenderFragment ThemeSettingsComponent { get; set; }
protected override async Task OnInitializedAsync()
{
@ -290,23 +248,16 @@
_isnavigation = page.IsNavigation.ToString();
_url = page.Url;
_ispersonalizable = page.IsPersonalizable.ToString();
_mode = (page.EditMode) ? "edit" : "view";
_themetype = page.ThemeType;
if (_themetype == PageState.Site.DefaultThemeType)
if (string.IsNullOrEmpty(_themetype))
{
_themetype = "-";
_themetype = PageState.Site.DefaultThemeType;
}
_layouts = ThemeService.GetLayoutControls(_themeList, page.ThemeType);
_layouttype = page.LayoutType;
if (_layouttype == PageState.Site.DefaultLayoutType)
{
_layouttype = "-";
}
_containers = ThemeService.GetContainerControls(_themeList, page.ThemeType);
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
_containertype = page.DefaultContainerType;
if (string.IsNullOrEmpty(_containertype))
{
_containertype = "-";
_containertype = PageState.Site.DefaultContainerType;
}
_icon = page.Icon;
_permissions = page.Permissions;
@ -316,12 +267,14 @@
_modifiedon = page.ModifiedOn;
_deletedby = page.DeletedBy;
_deletedon = page.DeletedOn;
ThemeSettings();
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading Page {PageId} {Error}", _pageId, ex.Message);
AddModuleMessage("Error Loading Page", MessageType.Error);
AddModuleMessage(Localizer["Error Loading Page"], MessageType.Error);
}
}
@ -333,7 +286,7 @@
_children = new List<Page>();
if (_parentid == "-1")
{
foreach(Page p in PageState.Pages.Where(item => item.ParentId == null))
foreach (Page p in PageState.Pages.Where(item => item.ParentId == null))
{
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions))
{
@ -364,7 +317,7 @@
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading Child Pages For Parent {PageId} {Error}", _parentid, ex.Message);
AddModuleMessage("Error Loading Child Pages For Parent", MessageType.Error);
AddModuleMessage(Localizer["Error Loading Child Pages For Parent"], MessageType.Error);
}
}
@ -373,24 +326,34 @@
try
{
_themetype = (string)e.Value;
if (_themetype != "-")
{
_layouts = ThemeService.GetLayoutControls(_themeList, _themetype);
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
}
else
{
_layouts = new List<ThemeControl>();
_containers = new List<ThemeControl>();
}
_layouttype = "-";
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
_containertype = "-";
ThemeSettings();
StateHasChanged();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading Pane Layouts For Theme {ThemeType} {Error}", _themetype, ex.Message);
AddModuleMessage("Error Loading Pane Layouts For Theme", MessageType.Error);
AddModuleMessage(Localizer["Error Loading Pane Layouts For Theme"], MessageType.Error);
}
}
private void ThemeSettings()
{
_themeSettingsType = null;
var theme = _themeList.FirstOrDefault(item => item.Themes.Any(themecontrol => themecontrol.TypeName.Equals(_themetype)));
if (theme != null && !string.IsNullOrEmpty(theme.ThemeSettingsType))
{
_themeSettingsType = Type.GetType(theme.ThemeSettingsType);
if (_themeSettingsType != null)
{
ThemeSettingsComponent = builder =>
{
builder.OpenComponent(0, _themeSettingsType);
builder.AddComponentReferenceCapture(1, inst => { _themeSettings = Convert.ChangeType(inst, _themeSettingsType); });
builder.CloseComponent();
};
}
}
}
@ -399,7 +362,7 @@
Page page = null;
try
{
if (_name != string.Empty)
if (_name != string.Empty && !string.IsNullOrEmpty(_themetype) && _containertype != "-")
{
page = PageState.Pages.FirstOrDefault(item => item.PageId == _pageId);
string currentPath = page.Path;
@ -433,6 +396,13 @@
page.Path = parent.Path + "/" + Utilities.GetFriendlyUrl(_path);
}
}
if (!PagePathIsUnique(page.Path, page.SiteId, page.PageId, _pageList))
{
AddModuleMessage(Localizer["A page with path {0} already exists for the selected parent page. The page path needs to be unique for the selected parent.", _path], MessageType.Warning);
return;
}
if (_insert != "=")
{
Page child;
@ -456,17 +426,11 @@
}
page.IsNavigation = (_isnavigation == null || Boolean.Parse(_isnavigation));
page.Url = _url;
page.EditMode = (_mode == "edit");
page.ThemeType = (_themetype != "-") ? _themetype : string.Empty;
if (!string.IsNullOrEmpty(page.ThemeType) && page.ThemeType == PageState.Site.DefaultThemeType)
{
page.ThemeType = string.Empty;
}
page.LayoutType = (_layouttype != "-") ? _layouttype : string.Empty;
if (!string.IsNullOrEmpty(page.LayoutType) && page.LayoutType == PageState.Site.DefaultLayoutType)
{
page.LayoutType = string.Empty;
}
page.DefaultContainerType = (_containertype != "-") ? _containertype : string.Empty;
if (!string.IsNullOrEmpty(page.DefaultContainerType) && page.DefaultContainerType == PageState.Site.DefaultContainerType)
{
@ -498,18 +462,47 @@
}
}
if (_themeSettingsType != null && _themeSettings is ISettingsControl themeSettingsControl)
{
await themeSettingsControl.UpdateSettings();
}
await logger.LogInformation("Page Saved {Page}", page);
NavigationManager.NavigateTo(NavigateUrl(page.Path));
if (PageState.QueryString.ContainsKey("cp"))
{
NavigationManager.NavigateTo(NavigateUrl(PageState.Pages.First(item => item.PageId == int.Parse(PageState.QueryString["cp"])).Path));
}
else
{
NavigationManager.NavigateTo(NavigateUrl(page.Path));
}
}
else
{
AddModuleMessage("You Must Provide Page Name", MessageType.Warning);
AddModuleMessage(Localizer["You Must Provide Page Name, Theme, and Container"], MessageType.Warning);
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Saving Page {Page} {Error}", page, ex.Message);
AddModuleMessage("Error Saving Page", MessageType.Error);
AddModuleMessage(Localizer["Error Saving Page"], MessageType.Error);
}
}
private void Cancel()
{
if (PageState.QueryString.ContainsKey("cp"))
{
NavigationManager.NavigateTo(NavigateUrl(PageState.Pages.First(item => item.PageId == int.Parse(PageState.QueryString["cp"])).Path));
}
else
{
NavigationManager.NavigateTo(NavigateUrl());
}
}
private static bool PagePathIsUnique(string pagePath, int siteId, int pageId, List<Page> existingPages)
{
return !existingPages.Any(page => page.SiteId == siteId && page.Path == pagePath && page.PageId != pageId);
}
}

View File

@ -1,21 +1,22 @@
@namespace Oqtane.Modules.Admin.Pages
@namespace Oqtane.Modules.Admin.Pages
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IPageService PageService
@inject IStringLocalizer<Index> Localizer
@if (PageState.Pages != null)
{
<ActionLink Action="Add" Text="Add Page" />
<ActionLink Action="Add" Text="Add Page" ResourceKey="AddPage" />
<Pager Items="@PageState.Pages.Where(item => !item.IsDeleted)">
<Header>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>Name</th>
<th>@Localizer["Name"]</th>
</Header>
<Row>
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.PageId.ToString())" /></td>
<td><ActionDialog Header="Delete Page" Message="@("Are You Sure You Wish To Delete The " + context.Name + " Page?")" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeletePage(context))" /></td>
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.PageId.ToString())" ResourceKey="EditPage" /></td>
<td><ActionDialog Header="Delete Page" Message="@Localizer["Are You Sure You Wish To Delete The {0} Page?", context.Name]" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeletePage(context))" ResourceKey="DeletePage" /></td>
<td>@(new string('-', context.Level * 2))@(context.Name)</td>
</Row>
</Pager>
@ -37,7 +38,7 @@
catch (Exception ex)
{
await logger.LogError(ex, "Error Deleting Page {Page} {Error}", page, ex.Message);
AddModuleMessage("Error Deleting Page", MessageType.Error);
AddModuleMessage(Localizer["Error Deleting Page"], MessageType.Error);
}
}
}

View File

@ -1,90 +1,99 @@
@namespace Oqtane.Modules.Admin.Profiles
@namespace Oqtane.Modules.Admin.Profiles
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IProfileService ProfileService
@inject IStringLocalizer<Edit> Localizer
<table class="table table-borderless">
<tr>
<td>
<Label For="name" HelpText="The name of this field">Name: </Label>
</td>
<td>
<input id="name" class="form-control" @bind="@_name" />
</td>
</tr>
<tr>
<td>
<Label For="title" HelpText="The title of the field">Title: </Label>
</td>
<td>
<input id="title" class="form-control" @bind="@_title" />
</td>
</tr>
<tr>
<td>
<Label For="description" HelpText="What the profile field is">Description: </Label>
</td>
<td>
<textarea id="description" class="form-control" @bind="@_description" rows="5"></textarea>
</td>
</tr>
<tr>
<td>
<Label For="category" HelpText="What larger category does this field belong to">Category: </Label>
</td>
<td>
<input id="category" class="form-control" @bind="@_category" />
</td>
</tr>
<tr>
<td>
<Label For="order" HelpText="What place is this field in a larger category list">Order: </Label>
</td>
<td>
<input id="order" class="form-control" @bind="@_vieworder" />
</td>
</tr>
<tr>
<td>
<Label For="length" HelpText="What is the max amount of characters should this field accept">Length: </Label>
</td>
<td>
<input id="length" class="form-control" @bind="@_maxlength" />
</td>
</tr>
<tr>
<td>
<Label For="defaultVal" HelpText="What value do you want this field to start with">Default Value: </Label>
</td>
<td>
<input id="defaultVal" class="form-control" @bind="@_defaultvalue" />
</td>
</tr>
<tr>
<td>
<Label For="required" HelpText="Is a user required to input something into this field?">Required? </Label>
</td>
<td>
<select id="required" class="form-control" @bind="@_isrequired">
<option value="True">Yes</option>
<option value="False">No</option>
</select>
</td>
</tr>
<tr>
<td>
<Label For="private" HelpText="Is this field private?">Private? </Label>
</td>
<td>
<select id="private" class="form-control" @bind="@_isprivate">
<option value="True">Yes</option>
<option value="False">No</option>
</select>
</td>
</tr>
</table>
<button type="button" class="btn btn-success" @onclick="SaveProfile">Save</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink>
<table class="table table-borderless">
<tr>
<td>
<Label For="name" HelpText="The name of this profile item" ResourceKey="Name">Name: </Label>
</td>
<td>
<input id="name" class="form-control" @bind="@_name" />
</td>
</tr>
<tr>
<td>
<Label For="title" HelpText="The title of the profile item to display to the user" ResourceKey="Title">Title: </Label>
</td>
<td>
<input id="title" class="form-control" @bind="@_title" />
</td>
</tr>
<tr>
<td>
<Label For="description" HelpText="The help text displayed to the user for this profile item" ResourceKey="Description">Description: </Label>
</td>
<td>
<textarea id="description" class="form-control" @bind="@_description" rows="5"></textarea>
</td>
</tr>
<tr>
<td>
<Label For="category" HelpText="The category of this profile item (for grouping)" ResourceKey="Category">Category: </Label>
</td>
<td>
<input id="category" class="form-control" @bind="@_category" />
</td>
</tr>
<tr>
<td>
<Label For="order" HelpText="The index order of where this profile item should be displayed" ResourceKey="Order">Order: </Label>
</td>
<td>
<input id="order" class="form-control" @bind="@_vieworder" />
</td>
</tr>
<tr>
<td>
<Label For="length" HelpText="The max number of characters this profile item should accept (enter zero for unlimited)" ResourceKey="Length">Length: </Label>
</td>
<td>
<input id="length" class="form-control" @bind="@_maxlength" />
</td>
</tr>
<tr>
<td>
<Label For="defaultVal" HelpText="The default value for this profile item" ResourceKey="DefaultValue">Default Value: </Label>
</td>
<td>
<input id="defaultVal" class="form-control" @bind="@_defaultvalue" />
</td>
</tr>
<tr>
<td>
<Label For="options" HelpText="A comma delimited list of options the user can select from" ResourceKey="Options">Options: </Label>
</td>
<td>
<input id="options" class="form-control" @bind="@_options" />
</td>
</tr>
<tr>
<td>
<Label For="required" HelpText="Should a user be required to provide a value for this profile item?" ResourceKey="Required">Required? </Label>
</td>
<td>
<select id="required" class="form-control" @bind="@_isrequired">
<option value="True">@Localizer["Yes"]</option>
<option value="False">@Localizer["No"]</option>
</select>
</td>
</tr>
<tr>
<td>
<Label For="private" HelpText="Should this profile item be visible to all users?" ResourceKey="Private">Private? </Label>
</td>
<td>
<select id="private" class="form-control" @bind="@_isprivate">
<option value="True">@Localizer["Yes"]</option>
<option value="False">@Localizer["No"]</option>
</select>
</td>
</tr>
</table>
<button type="button" class="btn btn-success" @onclick="SaveProfile">@Localizer["Save"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@Localizer["Cancel"]</NavLink>
@code {
private int _profileid = -1;
@ -95,6 +104,7 @@
private string _vieworder = "0";
private string _maxlength = "0";
private string _defaultvalue = string.Empty;
private string _options = string.Empty;
private string _isrequired = "False";
private string _isprivate = "False";
@ -119,6 +129,7 @@
_vieworder = profile.ViewOrder.ToString();
_maxlength = profile.MaxLength.ToString();
_defaultvalue = profile.DefaultValue;
_options = profile.Options;
_isrequired = profile.IsRequired.ToString();
_isprivate = profile.IsPrivate.ToString();
}
@ -127,7 +138,7 @@
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading Profile {ProfileId} {Error}", _profileid, ex.Message);
AddModuleMessage("Error Loading Profile", MessageType.Error);
AddModuleMessage(Localizer["Error Loading Profile"], MessageType.Error);
}
}
@ -145,6 +156,7 @@
profile = new Profile();
}
profile.SiteId = PageState.Site.SiteId;
profile.Name = _name;
profile.Title = _title;
profile.Description = _description;
@ -152,12 +164,14 @@
profile.ViewOrder = int.Parse(_vieworder);
profile.MaxLength = int.Parse(_maxlength);
profile.DefaultValue = _defaultvalue;
profile.Options = _options;
profile.IsRequired = (_isrequired == null ? false : Boolean.Parse(_isrequired));
profile.IsPrivate = (_isprivate == null ? false : Boolean.Parse(_isprivate));
if (_profileid != -1)
{
profile = await ProfileService.UpdateProfileAsync(profile);
}else
}
else
{
profile = await ProfileService.AddProfileAsync(profile);
}
@ -168,7 +182,7 @@
catch (Exception ex)
{
await logger.LogError(ex, "Error Saving Profile {ProfleId} {Error}", _profileid, ex.Message);
AddModuleMessage("Error Saving Profile", MessageType.Error);
AddModuleMessage(Localizer["Error Saving Profile"], MessageType.Error);
}
}
}

View File

@ -1,24 +1,25 @@
@namespace Oqtane.Modules.Admin.Profiles
@namespace Oqtane.Modules.Admin.Profiles
@inherits ModuleBase
@inject IProfileService ProfileService
@inject IStringLocalizer<Index> Localizer
@if (_profiles == null)
{
<p><em>Loading...</em></p>
<p><em>@Localizer["Loading..."]</em></p>
}
else
{
<ActionLink Action="Add" Security="SecurityAccessLevel.Admin" Text="Add Profile" />
<ActionLink Action="Add" Security="SecurityAccessLevel.Admin" Text="Add Profile" ResourceKey="AddProfile" />
<Pager Items="@_profiles">
<Header>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>Name</th>
<th>@Localizer["Name"]</th>
</Header>
<Row>
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.ProfileId.ToString())" /></td>
<td><ActionDialog Header="Delete Profile" Message="@("Are You Sure You Wish To Delete " + context.Name + "?")" Action="Delete" Class="btn btn-danger" OnClick="@(async () => await DeleteProfile(context.ProfileId))" /></td>
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.ProfileId.ToString())" ResourceKey="EditProfile" /></td>
<td><ActionDialog Header="Delete Profile" Message="@Localizer["Are You Sure You Wish To Delete {0}?", context.Name]" Action="Delete" Class="btn btn-danger" OnClick="@(async () => await DeleteProfile(context.ProfileId))" ResourceKey="DeleteProfile" /></td>
<td>@context.Name</td>
</Row>
</Pager>
@ -29,9 +30,9 @@ else
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
protected override async Task OnInitializedAsync()
protected override async Task OnParametersSetAsync()
{
_profiles = await ProfileService.GetProfilesAsync(PageState.Site.SiteId);
await GetProfilesAsync();
}
private async Task DeleteProfile(int profileId)
@ -40,12 +41,22 @@ else
{
await ProfileService.DeleteProfileAsync(profileId);
await logger.LogInformation("Profile Deleted {ProfileId}", profileId);
AddModuleMessage("Profile Deleted", MessageType.Success);
AddModuleMessage(Localizer["Profile Deleted"], MessageType.Success);
await GetProfilesAsync();
StateHasChanged();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Deleting Profile {ProfileId} {Error}", profileId, ex.Message);
AddModuleMessage("Error Deleting Profile", MessageType.Error);
AddModuleMessage(Localizer["Error Deleting Profile"], MessageType.Error);
}
}
private async Task GetProfilesAsync()
{
_profiles = await ProfileService.GetProfilesAsync(PageState.Site.SiteId);
}
}

View File

@ -1,16 +1,17 @@
@namespace Oqtane.Modules.Admin.RecycleBin
@namespace Oqtane.Modules.Admin.RecycleBin
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IPageModuleService PageModuleService
@inject IModuleService ModuleService
@inject IPageService PageService
@inject IStringLocalizer<Index> Localizer
<TabStrip>
<TabPanel Name="Pages">
<TabPanel Name="Pages" ResourceKey="Pages">
@if (_pages == null)
{
<br />
<p>No Deleted Pages</p>
<p>@Localizer["No Deleted Pages"]</p>
}
else
{
@ -18,25 +19,31 @@
<Header>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>Name</th>
<th>Deleted By</th>
<th>Deleted On</th>
<th>@Localizer["Name"]</th>
<th>@Localizer["Deleted By"]</th>
<th>@Localizer["Deleted On"]</th>
</Header>
<Row>
<td><button @onclick="@(() => RestorePage(context))" class="btn btn-info" title="Restore">Restore</button></td>
<td><ActionDialog Header="Delete Page" Message="@("Are You Sure You Wish To Permanently Delete The " + context.Name + " Page?")" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeletePage(context))" /></td>
<td><ActionDialog Header="Delete Page" Message="@Localizer["Are You Sure You Wish To Permanently Delete The {0} Page?", context.Name]" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeletePage(context))" ResourceKey="DeletePage" /></td>
<td>@context.Name</td>
<td>@context.DeletedBy</td>
<td>@context.DeletedOn</td>
</Row>
</Pager>
@if (_pages.Any())
{
<div style="text-align:right;">
<ActionDialog Header="Delete All Pages" Message="Are You Sure You Wish To Permanently Delete All Pages?" Action="Delete All Pages" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteAllPages())" ResourceKey="DeleteAllPages" />
</div>
}
}
</TabPanel>
<TabPanel Name="Modules">
<TabPanel Name="Modules" ResourceKey="Modules">
@if (_modules == null)
{
<br />
<p>No Deleted Modules</p>
<p>@Localizer["No Deleted Modules"]</p>
}
else
{
@ -44,20 +51,27 @@
<Header>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>Page</th>
<th>Module</th>
<th>Deleted By</th>
<th>Deleted On</th>
<th>@Localizer["Page"]</th>
<th>@Localizer["Module"]</th>
<th>@Localizer["Deleted By"]</th>
<th>@Localizer["Deleted On"]</th>
</Header>
<Row>
<td><button @onclick="@(() => RestoreModule(context))" class="btn btn-info" title="Restore">Restore</button></td>
<td><ActionDialog Header="Delete Module" Message="@("Are You Sure You Wish To Permanently Delete The " + context.Title + " Module?")" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteModule(context))" /></td>
<td><button @onclick="@(() => RestoreModule(context))" class="btn btn-info" title="Restore">@Localizer["Restore"]</button></td>
<td><ActionDialog Header="Delete Module" Message="@Localizer["Are You Sure You Wish To Permanently Delete The {0} Module?", context.Title]" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteModule(context))" ResourceKey="DeleteModule" /></td>
<td>@PageState.Pages.Find(item => item.PageId == context.PageId).Name</td>
<td>@context.Title</td>
<td>@context.DeletedBy</td>
<td>@context.DeletedOn</td>
</Row>
</Pager>
@if (_modules.Any())
{
<div style="text-align:right;">
<ActionDialog Header="Delete All Modules" Message="Are You Sure You Wish To Permanently Delete All Modules?" Action="Delete All Modules" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteAllModules())" ResourceKey="DeleteAllModules" />
</div>
}
}
</TabPanel>
</TabStrip>
@ -77,7 +91,7 @@
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading Deleted Pages Or Modules {Error}", ex.Message);
AddModuleMessage("Error Loading Deleted Pages Or Modules", MessageType.Error);
AddModuleMessage(Localizer["Error Loading Deleted Pages Or Modules"], MessageType.Error);
}
}
@ -104,7 +118,7 @@
catch (Exception ex)
{
await logger.LogError(ex, "Error Restoring Deleted Page {Page} {Error}", page, ex.Message);
AddModuleMessage("Error Restoring Deleted Page", MessageType.Error);
AddModuleMessage(Localizer["Error Restoring Deleted Page"], MessageType.Error);
}
}
@ -125,6 +139,28 @@
}
}
private async Task DeleteAllPages()
{
try
{
foreach (Page page in _pages)
{
await PageService.DeletePageAsync(page.PageId);
await logger.LogInformation("Page Permanently Deleted {Page}", page);
}
await logger.LogInformation("Pages Permanently Deleted");
await Load();
StateHasChanged();
NavigationManager.NavigateTo(NavigateUrl());
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Permanently Deleting Pages {Error}", ex.Message);
AddModuleMessage(ex.Message, MessageType.Error);
}
}
private async Task RestoreModule(Module module)
{
try
@ -139,7 +175,7 @@
catch (Exception ex)
{
await logger.LogError(ex, "Error Restoring Deleted Module {Module} {Error}", module, ex.Message);
AddModuleMessage("Error Restoring Deleted Module", MessageType.Error);
AddModuleMessage(Localizer["Error Restoring Deleted Module"], MessageType.Error);
}
}
@ -150,12 +186,12 @@
await PageModuleService.DeletePageModuleAsync(module.PageModuleId);
// check if there are any remaining module instances in the site
_modules = await ModuleService.GetModulesAsync(PageState.Site.SiteId);
if (!_modules.Exists(item => item.ModuleId == module.ModuleId))
{
await ModuleService.DeleteModuleAsync(module.ModuleId);
}
await logger.LogInformation("Module Permanently Deleted {Module}", module);
await Load();
StateHasChanged();
@ -163,7 +199,34 @@
catch (Exception ex)
{
await logger.LogError(ex, "Error Permanently Deleting Module {Module} {Error}", module, ex.Message);
AddModuleMessage("Error Permanently Deleting Module", MessageType.Error);
AddModuleMessage(Localizer["Error Permanently Deleting Module"], MessageType.Error);
}
}
private async Task DeleteAllModules()
{
try
{
foreach (Module module in _modules)
{
await PageModuleService.DeletePageModuleAsync(module.PageModuleId);
// check if there are any remaining module instances in the site
_modules = await ModuleService.GetModulesAsync(PageState.Site.SiteId);
if (!_modules.Exists(item => item.ModuleId == module.ModuleId))
{
await ModuleService.DeleteModuleAsync(module.ModuleId);
}
}
await logger.LogInformation("Modules Permanently Deleted");
await Load();
StateHasChanged();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Permanently Deleting Modules {Error}", ex.Message);
AddModuleMessage(Localizer["Error Permanently Deleting Modules"], MessageType.Error);
}
}
}

View File

@ -1,7 +1,8 @@
@namespace Oqtane.Modules.Admin.Register
@namespace Oqtane.Modules.Admin.Register
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IUserService UserService
@inject IStringLocalizer<Index> Localizer
@if (PageState.Site.AllowRegistration)
{
@ -10,41 +11,41 @@
<text>...</text>
</Authorizing>
<Authorized>
<ModuleMessage Message="You Are Already Registered" Type="MessageType.Info" />
<ModuleMessage Message="@Localizer["You Are Already Registered"]" Type="MessageType.Info" />
</Authorized>
<NotAuthorized>
<ModuleMessage Message="Please Note That Registration Requires A Valid Email Address In Order To Verify Your Identity" Type="MessageType.Info" />
<ModuleMessage Message="@Localizer["Please Note That Registration Requires A Valid Email Address In Order To Verify Your Identity"]" Type="MessageType.Info" />
<div class="container">
<div class="form-group">
<label for="Username" class="control-label">Username: </label>
<label for="Username" class="control-label">@Localizer["Username:"] </label>
<input type="text" class="form-control" placeholder="Username" @bind="@_username" id="Username" />
</div>
<div class="form-group">
<label for="Password" class="control-label">Password: </label>
<label for="Password" class="control-label">@Localizer["Password:"] </label>
<input type="password" class="form-control" placeholder="Password" @bind="@_password" id="Password" />
</div>
<div class="form-group">
<label for="Confirm" class="control-label">Confirm Password: </label>
<label for="Confirm" class="control-label">@Localizer["Confirm Password:"] </label>
<input type="password" class="form-control" placeholder="Password" @bind="@_confirm" id="Confirm" />
</div>
<div class="form-group">
<label for="Email" class="control-label">Email: </label>
<label for="Email" class="control-label">@Localizer["Email:"] </label>
<input type="text" class="form-control" placeholder="Email" @bind="@_email" id="Email" />
</div>
<div class="form-group">
<label for="DisplayName" class="control-label">Full Name: </label>
<label for="DisplayName" class="control-label">@Localizer["Full Name:"] </label>
<input type="text" class="form-control" placeholder="Full Name" @bind="@_displayName" id="DisplayName" />
</div>
<button type="button" class="btn btn-primary" @onclick="Register">Register</button>
<button type="button" class="btn btn-secondary" @onclick="Cancel">Cancel</button>
<button type="button" class="btn btn-primary" @onclick="Register">@Localizer["Register"]</button>
<button type="button" class="btn btn-secondary" @onclick="Cancel">@Localizer["Cancel"]</button>
</div>
</NotAuthorized>
</AuthorizeView>
}
else
{
<ModuleMessage Message="Registration is Disabled For This Site" Type="MessageType.Info" />
<ModuleMessage Message="@Localizer["Registration is Disabled For This Site"]" Type="MessageType.Info" />
}
@code {
@ -79,28 +80,28 @@ else
if (user != null)
{
await logger.LogInformation("User Created {Username} {Email}", _username, _email);
AddModuleMessage("User Account Created. Please Check Your Email For Verification Instructions.", MessageType.Info);
AddModuleMessage(Localizer["User Account Created. Please Check Your Email For Verification Instructions."], MessageType.Info);
}
else
{
await logger.LogError("Error Adding User {Username} {Email}", _username, _email);
AddModuleMessage("Error Adding User. Please Ensure Password Meets Complexity Requirements And Username Is Not Already In Use.", MessageType.Error);
AddModuleMessage(Localizer["Error Adding User. Please Ensure Password Meets Complexity Requirements And Username Is Not Already In Use."], MessageType.Error);
}
}
else
{
AddModuleMessage("Passwords Entered Do Not Match", MessageType.Warning);
AddModuleMessage(Localizer["Passwords Entered Do Not Match"], MessageType.Warning);
}
}
else
{
AddModuleMessage("You Must Provide A Username, Password, and Email Address", MessageType.Warning);
AddModuleMessage(Localizer["You Must Provide A Username, Password, and Email Address"], MessageType.Warning);
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Adding User {Username} {Email} {Error}", _username, _email, ex.Message);
AddModuleMessage("Error Adding User", MessageType.Error);
AddModuleMessage(Localizer["Error Adding User"], MessageType.Error);
}
}

View File

@ -1,23 +1,24 @@
@namespace Oqtane.Modules.Admin.Reset
@namespace Oqtane.Modules.Admin.Reset
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IUserService UserService
@inject IStringLocalizer<Index> Localizer
<div class="container">
<div class="form-group">
<label for="Username" class="control-label">Username: </label>
<label for="Username" class="control-label">@Localizer["Username:"] </label>
<input type="text" class="form-control" placeholder="Username" @bind="@_username" readonly id="Username"/>
</div>
<div class="form-group">
<label for="Password" class="control-label">Password: </label>
<label for="Password" class="control-label">@Localizer["Password:"] </label>
<input type="password" class="form-control" placeholder="Password" @bind="@_password" id="Password"/>
</div>
<div class="form-group">
<label for="Confirm" class="control-label">Confirm Password: </label>
<label for="Confirm" class="control-label">@Localizer["Confirm Password:"] </label>
<input type="password" class="form-control" placeholder="Password" @bind="@_confirm" id="Confirm"/>
</div>
<button type="button" class="btn btn-primary" @onclick="Reset">Reset Password</button>
<button type="button" class="btn btn-secondary" @onclick="Cancel">Cancel</button>
<button type="button" class="btn btn-primary" @onclick="Reset">@Localizer["Reset Password"]</button>
<button type="button" class="btn btn-secondary" @onclick="Cancel">@Localizer["Cancel"]</button>
</div>
@code {
@ -63,23 +64,23 @@
else
{
await logger.LogError("Error Resetting User Password {Username}", _username);
AddModuleMessage("Error Resetting User Password. Please Ensure Password Meets Complexity Requirements.", MessageType.Error);
AddModuleMessage(Localizer["Error Resetting User Password. Please Ensure Password Meets Complexity Requirements."], MessageType.Error);
}
}
else
{
AddModuleMessage("Passwords Entered Do Not Match", MessageType.Warning);
AddModuleMessage(Localizer["Passwords Entered Do Not Match"], MessageType.Warning);
}
}
else
{
AddModuleMessage("You Must Provide A Username, Password, and Email Address", MessageType.Warning);
AddModuleMessage(Localizer["You Must Provide A Username, Password, and Email Address"], MessageType.Warning);
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Resetting User Password {Username} {Error}", _username, ex.Message);
AddModuleMessage("Error Resetting User Password", MessageType.Error);
AddModuleMessage(Localizer["Error Resetting User Password"], MessageType.Error);
}
}

View File

@ -1,12 +1,13 @@
@namespace Oqtane.Modules.Admin.Roles
@namespace Oqtane.Modules.Admin.Roles
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IRoleService RoleService
@inject IStringLocalizer<Add> Localizer
<table class="table table-borderless">
<tr>
<td>
<Label For="name" HelpText="Name Of The Role">Name:</Label>
<Label For="name" HelpText="Name Of The Role" ResourceKey="Name">Name:</Label>
</td>
<td>
<input id="name" class="form-control" @bind="@_name" />
@ -14,7 +15,7 @@
</tr>
<tr>
<td>
<Label For="description" HelpText="A Short Description Of The Role Which Describes Its Purpose">Description:</Label>
<Label For="description" HelpText="A Short Description Of The Role Which Describes Its Purpose" ResourceKey="Description">Description:</Label>
</td>
<td>
<textarea id="description" class="form-control" @bind="@_description" rows="5"></textarea>
@ -22,20 +23,20 @@
</tr>
<tr>
<td>
<Label For="isautoassigned" HelpText="Indicates Whether Or Not New Users Are Automatically Assigned To This Role">Auto Assigned?</Label>
<Label For="isautoassigned" HelpText="Indicates Whether Or Not New Users Are Automatically Assigned To This Role" ResourceKey="AutoAssigned">Auto Assigned?</Label>
</td>
<td>
<select id="isautoassigned" class="form-control" @bind="@_isautoassigned">
<option value="True">Yes</option>
<option value="False">No</option>
<option value="True">@Localizer["Yes"]</option>
<option value="False">@Localizer["No"]</option>
</select>
</td>
</tr>
</table>
<button type="button" class="btn btn-success" @onclick="SaveRole">Save</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink>
<button type="button" class="btn btn-success" @onclick="SaveRole">@Localizer["Save"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@Localizer["Cancel"]</NavLink>
@code {
@code {
private string _name = string.Empty;
private string _description = string.Empty;
private string _isautoassigned = "False";
@ -61,7 +62,7 @@
catch (Exception ex)
{
await logger.LogError(ex, "Error Adding Role {Role} {Error}", role, ex.Message);
AddModuleMessage("Error Adding Role", MessageType.Error);
AddModuleMessage(Localizer["Error Adding Role"], MessageType.Error);
}
}

View File

@ -1,12 +1,13 @@
@namespace Oqtane.Modules.Admin.Roles
@namespace Oqtane.Modules.Admin.Roles
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IRoleService RoleService
@inject IStringLocalizer<Edit> Localizer
<table class="table table-borderless">
<tr>
<td>
<Label For="name" HelpText="Name Of The Role">Name:</Label>
<Label For="name" HelpText="Name Of The Role" ResourceKey="Name">Name:</Label>
</td>
<td>
<input id="name" class="form-control" @bind="@_name" />
@ -14,7 +15,7 @@
</tr>
<tr>
<td>
<Label For="description" HelpText="A Short Description Of The Role Which Describes Its Purpose">Description:</Label>
<Label For="description" HelpText="A Short Description Of The Role Which Describes Its Purpose" ResourceKey="Description">Description:</Label>
</td>
<td>
<textarea id="description" class="form-control" @bind="@_description" rows="5"></textarea>
@ -22,18 +23,18 @@
</tr>
<tr>
<td>
<Label For="isautoassigned" HelpText="Indicates Whether Or Not New Users Are Automatically Assigned To This Role">Auto Assigned?</Label>
<Label For="isautoassigned" HelpText="Indicates Whether Or Not New Users Are Automatically Assigned To This Role" ResourceKey="AutoAssigned">Auto Assigned?</Label>
</td>
<td>
<select id="isautoassigned" class="form-control" @bind="@_isautoassigned">
<option value="True">Yes</option>
<option value="False">No</option>
<option value="True">@Localizer["Yes"]</option>
<option value="False">@Localizer["No"]</option>
</select>
</td>
</tr>
</table>
<button type="button" class="btn btn-success" @onclick="SaveRole">Save</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink>
<button type="button" class="btn btn-success" @onclick="SaveRole">@Localizer["Save"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@Localizer["Cancel"]</NavLink>
@code {
private int _roleid;
@ -59,7 +60,7 @@
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading Role {RoleId} {Error}", _roleid, ex.Message);
AddModuleMessage("Error Loading Role", MessageType.Error);
AddModuleMessage(Localizer["Error Loading Role"], MessageType.Error);
}
}
@ -80,7 +81,7 @@
catch (Exception ex)
{
await logger.LogError(ex, "Error Saving Role {Role} {Error}", role, ex.Message);
AddModuleMessage("Error Saving Role", MessageType.Error);
AddModuleMessage(Localizer["Error Saving Role"], MessageType.Error);
}
}
}

View File

@ -1,26 +1,27 @@
@namespace Oqtane.Modules.Admin.Roles
@namespace Oqtane.Modules.Admin.Roles
@inherits ModuleBase
@inject IRoleService RoleService
@inject IStringLocalizer<Index> Localizer
@if (_roles == null)
{
<p><em>Loading...</em></p>
<p><em>@Localizer["Loading..."]</em></p>
}
else
{
<ActionLink Action="Add" Text="Add Role" />
<ActionLink Action="Add" Text="Add Role" ResourceKey="AddRole" />
<Pager Items="@_roles">
<Header>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>Name</th>
<th>@Localizer["Name"]</th>
</Header>
<Row>
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.RoleId.ToString())" Disabled="@(context.IsSystem)" /></td>
<td><ActionDialog Header="Delete Role" Message="@("Are You Sure You Wish To Delete The " + context.Name + " Role?")" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteRole(context))" Disabled="@(context.IsSystem)" /></td>
<td><ActionLink Action="Users" Parameters="@($"id=" + context.RoleId.ToString())" /></td>
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.RoleId.ToString())" Disabled="@(context.IsSystem)" ResourceKey="Edit" /></td>
<td><ActionDialog Header="Delete Role" Message="@Localizer["Are You Sure You Wish To Delete The {0} Role?", context.Name]" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteRole(context))" Disabled="@(context.IsSystem)" ResourceKey="DeleteRole" /></td>
<td><ActionLink Action="Users" Parameters="@($"id=" + context.RoleId.ToString())" ResourceKey="Users" /></td>
<td>@context.Name</td>
</Row>
</Pager>
@ -47,7 +48,7 @@ else
catch (Exception ex)
{
await logger.LogError(ex, "Error Deleting Role {Role} {Error}", role, ex.Message);
AddModuleMessage("Error Deleting Role", MessageType.Error);
AddModuleMessage(Localizer["Error Deleting Role"], MessageType.Error);
}
}
}

View File

@ -1,18 +1,19 @@
@namespace Oqtane.Modules.Admin.Roles
@namespace Oqtane.Modules.Admin.Roles
@inherits ModuleBase
@inject IRoleService RoleService
@inject IUserRoleService UserRoleService
@inject IStringLocalizer<Users> Localizer
@if (userroles == null)
{
<p><em>Loading...</em></p>
<p><em>@Localizer["Loading..."]</em></p>
}
else
{
<table class="table table-borderless">
<tr>
<td>
<Label For="role" HelpText="The role you are assigning users to">Role: </Label>
<Label For="role" HelpText="The role you are assigning users to" ResourceKey="Role">Role: </Label>
</td>
<td>
<input id="role" class="form-control" @bind="@name" disabled />
@ -20,11 +21,11 @@ else
</tr>
<tr>
<td>
<Label For="user" HelpText="Select a user">User: </Label>
<Label For="user" HelpText="Select a user" ResourceKey="User">User: </Label>
</td>
<td>
<select id="user" class="form-control" @bind="@userid">
<option value="-1">&lt;Select User&gt;</option>
<option value="-1">&lt;@Localizer["Select User"]&gt;</option>
@foreach (UserRole userrole in users)
{
<option value="@(userrole.UserId)">@userrole.User.DisplayName</option>
@ -34,35 +35,35 @@ else
</tr>
<tr>
<td>
<Label For="effectiveDate" HelpText="The date that this role assignment is active">Effective Date: </Label>
<Label For="effectiveDate" HelpText="The date that this role assignment is active" ResourceKey="EffectiveDate">Effective Date: </Label>
</td>
<td>
<input id="effectiveDate" class="form-control" @bind="@effectivedate" />
<input type="date" id="effectiveDate" class="form-control" @bind="@effectivedate" />
</td>
</tr>
<tr>
<td>
<Label For="expiryDate" HelpText="The date that this role assignment expires">Expiry Date: </Label>
<Label For="expiryDate" HelpText="The date that this role assignment expires" ResourceKey="ExpiryDate">Expiry Date: </Label>
</td>
<td>
<input id="expiryDate" class="form-control" @bind="@expirydate" />
<input type="date" id="expiryDate" class="form-control" @bind="@expirydate" />
</td>
</tr>
</table>
<button type="button" class="btn btn-success" @onclick="SaveUserRole">Save</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink>
<button type="button" class="btn btn-success" @onclick="SaveUserRole">@Localizer["Save"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@Localizer["Cancel"]</NavLink>
<hr class="app-rule" />
<p align="center">
<Pager Items="@userroles">
<Header>
<th>Users</th>
<th>@Localizer["Users"]</th>
<th>&nbsp;</th>
</Header>
<Row>
<td>@context.User.DisplayName</td>
<td>
<button type="button" class="btn btn-danger" @onclick=@(async () => await DeleteUserRole(context.UserRoleId))>Delete</button>
<button type="button" class="btn btn-danger" @onclick=@(async () => await DeleteUserRole(context.UserRoleId))>@Localizer["Delete"]</button>
</td>
</Row>
</Pager>
@ -74,8 +75,8 @@ else
private string name = string.Empty;
private List<UserRole> users;
private int userid = -1;
private string effectivedate = string.Empty;
private string expirydate = string.Empty;
private DateTime? effectivedate = null;
private DateTime? expirydate = null;
private List<UserRole> userroles;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
@ -88,13 +89,16 @@ else
Role role = await RoleService.GetRoleAsync(roleid);
name = role.Name;
users = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId);
users = users.Where(item => item.Role.Name == Constants.RegisteredRole).ToList();
users = users
.Where(u => u.Role.Name == RoleNames.Registered)
.OrderBy(u => u.User.DisplayName)
.ToList();
await GetUserRoles();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading Users {Error}", ex.Message);
AddModuleMessage("Error Loading Users", MessageType.Error);
AddModuleMessage(Localizer["Error Loading Users"], MessageType.Error);
}
}
@ -108,7 +112,7 @@ else
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading User Roles {RoleId} {Error}", roleid, ex.Message);
AddModuleMessage("Error Loading User Roles", MessageType.Error);
AddModuleMessage(Localizer["Error Loading User Roles"], MessageType.Error);
}
}
@ -121,23 +125,8 @@ else
var userrole = userroles.Where(item => item.UserId == userid && item.RoleId == roleid).FirstOrDefault();
if (userrole != null)
{
if (string.IsNullOrEmpty(effectivedate))
{
userrole.EffectiveDate = null;
}
else
{
userrole.EffectiveDate = DateTime.Parse(effectivedate);
}
if (string.IsNullOrEmpty(expirydate))
{
userrole.ExpiryDate = null;
}
else
{
userrole.ExpiryDate = DateTime.Parse(expirydate);
}
userrole.EffectiveDate = effectivedate;
userrole.ExpiryDate = expirydate;
await UserRoleService.UpdateUserRoleAsync(userrole);
}
else
@ -145,41 +134,25 @@ else
userrole = new UserRole();
userrole.UserId = userid;
userrole.RoleId = roleid;
if (string.IsNullOrEmpty(effectivedate))
{
userrole.EffectiveDate = null;
}
else
{
userrole.EffectiveDate = DateTime.Parse(effectivedate);
}
if (string.IsNullOrEmpty(expirydate))
{
userrole.ExpiryDate = null;
}
else
{
userrole.ExpiryDate = DateTime.Parse(expirydate);
}
userrole.EffectiveDate = effectivedate;
userrole.ExpiryDate = expirydate;
await UserRoleService.AddUserRoleAsync(userrole);
}
await GetUserRoles();
await logger.LogInformation("User Assigned To Role {UserRole}", userrole);
AddModuleMessage("User Assigned To Role", MessageType.Success);
AddModuleMessage(Localizer["User Assigned To Role"], MessageType.Success);
}
else
{
AddModuleMessage("You Must Select A User", MessageType.Warning);
AddModuleMessage(Localizer["You Must Select A User"], MessageType.Warning);
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Saving User Roles {RoleId} {Error}", roleid, ex.Message);
AddModuleMessage("Error Saving User Roles", MessageType.Error);
AddModuleMessage(Localizer["Error Saving User Roles"], MessageType.Error);
}
}
@ -190,12 +163,12 @@ else
await UserRoleService.DeleteUserRoleAsync(UserRoleId);
await GetUserRoles();
await logger.LogInformation("User Removed From Role {UserRoleId}", UserRoleId);
AddModuleMessage("User Removed From Role", MessageType.Success);
AddModuleMessage(Localizer["User Removed From Role"], MessageType.Success);
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Removing User From Role {UserRoleId} {Error}", UserRoleId, ex.Message);
AddModuleMessage("Error Removing User From Role", MessageType.Error);
AddModuleMessage(Localizer["Error Removing User From Role"], MessageType.Error);
}
}
}

View File

@ -1,4 +1,4 @@
@namespace Oqtane.Modules.Admin.Site
@namespace Oqtane.Modules.Admin.Site
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject ISiteService SiteService
@ -6,13 +6,15 @@
@inject IAliasService AliasService
@inject IThemeService ThemeService
@inject ISettingService SettingService
@inject IStringLocalizer<Index> Localizer
@inject INotificationService NotificationService
@if (_themes != null)
@if (_initialized)
{
<table class="table table-borderless">
<tr>
<td>
<Label For="name" HelpText="Enter the site name">Name: </Label>
<Label For="name" HelpText="Enter the site name" ResourceKey="Name">Name: </Label>
</td>
<td>
<input id="name" class="form-control" @bind="@_name" />
@ -20,7 +22,7 @@
</tr>
<tr>
<td>
<Label For="tenant" HelpText="Enter the tenant for the site">Tenant: </Label>
<Label For="tenant" HelpText="Enter the tenant for the site" ResourceKey="Tenant">Tenant: </Label>
</td>
<td>
<input id="tenant" class="form-control" @bind="@_tenant" readonly />
@ -28,7 +30,7 @@
</tr>
<tr>
<td>
<Label For="alias" HelpText="Enter the alias for the server">Aliases: </Label>
<Label For="alias" HelpText="Enter the alias for the server" ResourceKey="Aliases">Aliases: </Label>
</td>
<td>
<textarea id="alias" class="form-control" @bind="@_urls" rows="3"></textarea>
@ -36,7 +38,32 @@
</tr>
<tr>
<td>
<Label For="logo" HelpText="Upload a logo for the site">Logo: </Label>
<Label For="allowRegister" HelpText="Do you want the users to be able to register for an account on the site" ResourceKey="AllowRegistration">Allow User Registration? </Label>
</td>
<td>
<select id="allowRegister" class="form-control" @bind="@_allowregistration">
<option value="True">@Localizer["Yes"]</option>
<option value="False">@Localizer["No"]</option>
</select>
</td>
</tr>
<tr>
<td>
<Label For="isDeleted" HelpText="Is this site deleted?" ResourceKey="IsDeleted">Is Deleted? </Label>
</td>
<td>
<select id="isDeleted" class="form-control" @bind="@_isdeleted">
<option value="True">@Localizer["Yes"]</option>
<option value="False">@Localizer["No"]</option>
</select>
</td>
</tr>
</table>
<Section Name="Appearance" Heading="Appearance" ResourceKey="Appearance">
<table class="table table-borderless">
<tr>
<td>
<Label For="logo" HelpText="Specify a logo for the site" ResourceKey="Logo">Logo: </Label>
</td>
<td>
<FileManager FileId="@_logofileid" Filter="@Constants.ImageFiles" @ref="_logofilemanager" />
@ -44,7 +71,7 @@
</tr>
<tr>
<td>
<Label For="favicon" HelpText="Select Your default icon">Favicon: </Label>
<Label For="favicon" HelpText="Specify a Favicon" ResourceKey="FavoriteIcon">Favicon: </Label>
</td>
<td>
<FileManager FileId="@_faviconfileid" Filter="ico" @ref="_faviconfilemanager" />
@ -52,49 +79,25 @@
</tr>
<tr>
<td>
<Label For="defaultTheme" HelpText="Select the sites default theme">Default Theme: </Label>
<Label For="defaultTheme" HelpText="Select the sites default theme" ResourceKey="DefaultTheme">Default Theme: </Label>
</td>
<td>
<select id="defaultTheme" class="form-control" @onchange="(e => ThemeChanged(e))">
<option value="-">&lt;Select Theme&gt;</option>
<select id="defaultTheme" class="form-control" value="@_themetype" @onchange="(e => ThemeChanged(e))">
<option value="-">&lt;@Localizer["Select Theme"]&gt;</option>
@foreach (var theme in _themes)
{
if (theme.TypeName == _themetype)
{
<option value="@theme.TypeName" selected>@theme.Name</option>
}
else
{
<option value="@theme.TypeName">@theme.Name</option>
}
<option value="@theme.TypeName">@theme.Name</option>
}
</select>
</td>
</tr>
@if (_layouts.Count > 0)
{
<tr>
<td>
<Label For="defaultLayout" HelpText="Select the sites default layout">Default Layout: </Label>
</td>
<td>
<select id="defaultLayout" class="form-control" @bind="@_layouttype">
<option value="-">&lt;Select Layout&gt;</option>
@foreach (var layout in _layouts)
{
<option value="@(layout.TypeName)">@(layout.Name)</option>
}
</select>
</td>
</tr>
}
<tr>
<td>
<Label For="defaultContainer" HelpText="Select the default container for the site">Default Container: </Label>
<Label For="defaultContainer" HelpText="Select the default container for the site" ResourceKey="DefaultContainer">Default Container: </Label>
</td>
<td>
<select id="defaultContainer" class="form-control" @bind="@_containertype">
<option value="-">&lt;Select Container&gt;</option>
<option value="-">&lt;@Localizer["Select Container"]&gt;</option>
@foreach (var container in _containers)
{
<option value="@container.TypeName">@container.Name</option>
@ -104,33 +107,31 @@
</tr>
<tr>
<td>
<Label For="allowRegister" HelpText="Do you want the users to be able to register for an account on the site">Allow User Registration? </Label>
<Label For="defaultAdminContainer" HelpText="Select the default admin container for the site" ResourceKey="DefaultAdminContainer">Default Admin Container: </Label>
</td>
<td>
<select id="allowRegister" class="form-control" @bind="@_allowregistration">
<option value="True">Yes</option>
<option value="False">No</option>
</select>
</td>
</tr>
<tr>
<td>
<Label For="isDeleted" HelpText="Is this site deleted?">Is Deleted? </Label>
</td>
<td>
<select id="isDeleted" class="form-control" @bind="@_isdeleted">
<option value="True">Yes</option>
<option value="False">No</option>
<select id="defaultAdminContainer" class="form-control" @bind="@_admincontainertype">
<option value="-">&lt;@Localizer["Select Container"]&gt;</option>
<option value="">&lt;@Localizer["Default Admin Container"]&gt;</option>
@foreach (var container in _containers)
{
<option value="@container.TypeName">@container.Name</option>
}
</select>
</td>
</tr>
</table>
<Section Name="SMTP" Heading="SMTP Settings">
</Section>
<Section Name="SMTP" Heading="SMTP Settings" ResourceKey="SMTPSettings">
<table class="table table-borderless">
<tr>
<td colspan="2">
<strong>@Localizer["Please Note That SMTP Requires The Notification Job To Be Enabled In Scheduled Jobs"]</strong>
</td>
</tr>
<tr>
<td>
<Label For="host" HelpText="Enter the host name of the server">Host: </Label>
<Label For="host" HelpText="Enter the host name of the SMTP server" ResourceKey="Host">Host: </Label>
</td>
<td>
<input id="host" class="form-control" @bind="@_smtphost" />
@ -138,7 +139,7 @@
</tr>
<tr>
<td>
<Label For="port" HelpText="Enter the port number for the server">Port: </Label>
<Label For="port" HelpText="Enter the port number for the SMTP server. Please note this field is required if you provide a host name." ResourceKey="Port">Port: </Label>
</td>
<td>
<input id="port" class="form-control" @bind="@_smtpport" />
@ -146,15 +147,18 @@
</tr>
<tr>
<td>
<Label For="enabledSSl" HelpText="Specifiy if SSL is enabled for your server">SSL Enabled: </Label>
<Label For="enabledSSl" HelpText="Specify if SSL is required for your SMTP server" ResourceKey="UseSsl">SSL Enabled: </Label>
</td>
<td>
<input id="enabledSSl" class="form-control" @bind="@_smtpssl" />
<select id="enabledSSl" class="form-control" @bind="@_smtpssl">
<option value="True">@Localizer["Yes"]</option>
<option value="False">@Localizer["No"]</option>
</select>
</td>
</tr>
<tr>
<td>
<Label For="username" HelpText="Enter the username for the server">Username: </Label>
<Label For="username" HelpText="Enter the username for your SMTP account" ResourceKey="SmptUsername">Username: </Label>
</td>
<td>
<input id="username" class="form-control" @bind="@_smtpusername" />
@ -162,30 +166,40 @@
</tr>
<tr>
<td>
<Label For="password" HelpText="Enter the password for the server">Password: </Label>
<Label For="password" HelpText="Enter the password for your SMTP account" ResourceKey="SmtpPassword">Password: </Label>
</td>
<td>
<input id="password" type="password" class="form-control" @bind="@_smtppassword" />
</td>
</tr>
<tr>
<td>
<Label For="sender" HelpText="Enter the email which emails will be sent from. Please note that this email address may need to be authorized with the SMTP server." ResourceKey="SmptSender">Email Sender: </Label>
</td>
<td>
<input id="sender" class="form-control" @bind="@_smtpsender" />
</td>
</tr>
</table>
<button type="button" class="btn btn-secondary" @onclick="SendEmail">@Localizer["Test SMTP Configuration"]</button>
<br /><br />
</Section>
<Section Name="PWA" Heading="Progressive Web Application Settings">
<Section Name="PWA" Heading="Progressive Web Application Settings" ResourceKey="PWASettings">
<table class="table table-borderless">
<tr>
<td>
<Label For="isEnabled" HelpText="Select whether you would like this site to be available as a Progressive Web Application (PWA)">Is Enabled? </Label>
<Label For="isEnabled" HelpText="Select whether you would like this site to be available as a Progressive Web Application (PWA)" ResourceKey="EnablePWA">Is Enabled? </Label>
</td>
<td>
<select id="isEnabled" class="form-control" @bind="@_pwaisenabled">
<option value="True">Yes</option>
<option value="False">No</option>
<option value="True">@Localizer["Yes"]</option>
<option value="False">@Localizer["No"]</option>
</select>
</td>
</tr>
<tr>
<td>
<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." ResourceKey="PwaApplicationIcon">App Icon: </Label>
</td>
<td>
<FileManager FileId="@_pwaappiconfileid" Filter="png" @ref="_pwaappiconfilemanager" />
@ -193,7 +207,7 @@
</tr>
<tr>
<td>
<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." ResourceKey="PwaSplashIcon">Splash Icon: </Label>
</td>
<td>
<FileManager FileId="@_pwasplashiconfileid" Filter="png" @ref="_pwasplashiconfilemanager" />
@ -203,17 +217,16 @@
</Section>
<br />
<button type="button" class="btn btn-success" @onclick="SaveSite">Save</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink>
<button type="button" class="btn btn-success" @onclick="SaveSite">@Localizer["Save"]</button>
<br />
<br />
<AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon" DeletedBy="@_deletedby" DeletedOn="@_deletedon"></AuditInfo>
}
@code {
private bool _initialized = false;
private List<Theme> _themeList;
private List<ThemeControl> _themes = new List<ThemeControl>();
private List<ThemeControl> _layouts = new List<ThemeControl>();
private List<ThemeControl> _containers = new List<ThemeControl>();
private string _name = string.Empty;
private List<Tenant> _tenantList;
@ -225,14 +238,15 @@
private int _faviconfileid = -1;
private FileManager _faviconfilemanager;
private string _themetype = "-";
private string _layouttype = "-";
private string _containertype = "-";
private string _admincontainertype = "-";
private string _allowregistration;
private string _smtphost = string.Empty;
private string _smtpport = string.Empty;
private string _smtpssl = string.Empty;
private string _smtpssl = "False";
private string _smtpusername = string.Empty;
private string _smtppassword = string.Empty;
private string _smtpsender = string.Empty;
private string _pwaisenabled;
private int _pwaappiconfileid = -1;
private FileManager _pwaappiconfilemanager;
@ -276,18 +290,18 @@
_themes = ThemeService.GetThemeControls(_themeList);
_themetype = site.DefaultThemeType;
_layouts = ThemeService.GetLayoutControls(_themeList, _themetype);
_layouttype = site.DefaultLayoutType;
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
_containertype = site.DefaultContainerType;
_admincontainertype = site.AdminContainerType;
_allowregistration = site.AllowRegistration.ToString();
var settings = await SettingService.GetSiteSettingsAsync(site.SiteId);
_smtphost = SettingService.GetSetting(settings, "SMTPHost", string.Empty);
_smtpport = SettingService.GetSetting(settings, "SMTPPort", string.Empty);
_smtpssl = SettingService.GetSetting(settings, "SMTPSSL", string.Empty);
_smtpssl = SettingService.GetSetting(settings, "SMTPSSL", "False");
_smtpusername = SettingService.GetSetting(settings, "SMTPUsername", string.Empty);
_smtppassword = SettingService.GetSetting(settings, "SMTPPassword", string.Empty);
_smtpsender = SettingService.GetSetting(settings, "SMTPSender", string.Empty);
_pwaisenabled = site.PwaIsEnabled.ToString();
@ -318,6 +332,8 @@
_deletedby = site.DeletedBy;
_deletedon = site.DeletedOn;
_isdeleted = site.IsDeleted.ToString();
_initialized = true;
}
}
catch (Exception ex)
@ -334,22 +350,20 @@
_themetype = (string)e.Value;
if (_themetype != "-")
{
_layouts = ThemeService.GetLayoutControls(_themeList, _themetype);
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
}
else
{
_layouts = new List<ThemeControl>();
_containers = new List<ThemeControl>();
}
_layouttype = "-";
_containertype = "-";
_admincontainertype = "";
StateHasChanged();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading Pane Layouts For Theme {ThemeType} {Error}", _themetype, ex.Message);
AddModuleMessage("Error Loading Pane Layouts For Theme", MessageType.Error);
AddModuleMessage(Localizer["Error Loading Pane Layouts For Theme"], MessageType.Error);
}
}
@ -357,7 +371,7 @@
{
try
{
if (_name != string.Empty && _urls != string.Empty && _themetype != "-" && (_layouts.Count == 0 || _layouttype != "-") && _containertype != "-")
if (_name != string.Empty && _urls != string.Empty && _themetype != "-" && _containertype != "-")
{
var unique = true;
foreach (string name in _urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
@ -381,9 +395,16 @@
site.LogoFileId = logofileid;
}
var faviconFieldId = _faviconfilemanager.GetFileId();
if (faviconFieldId != -1)
{
site.FaviconFileId = faviconFieldId;
}
site.DefaultThemeType = _themetype;
site.DefaultLayoutType = (_layouttype == "-" ? string.Empty : _layouttype);
site.DefaultContainerType = _containertype;
site.AdminContainerType = _admincontainertype;
site.AllowRegistration = (_allowregistration == null ? true : Boolean.Parse(_allowregistration));
site.IsDeleted = (_isdeleted == null ? true : Boolean.Parse(_isdeleted));
@ -431,27 +452,59 @@
SettingService.SetSetting(settings, "SMTPSSL", _smtpssl);
SettingService.SetSetting(settings, "SMTPUsername", _smtpusername);
SettingService.SetSetting(settings, "SMTPPassword", _smtppassword);
SettingService.SetSetting(settings, "SMTPSender", _smtpsender);
await SettingService.UpdateSiteSettingsAsync(settings, site.SiteId);
await logger.LogInformation("Site Saved {Site}", site);
NavigationManager.NavigateTo(NavigateUrl());
await logger.LogInformation("Site Settings Saved {Site}", site);
AddModuleMessage(Localizer["Site Settings Saved"], MessageType.Success);
}
}
else
{
AddModuleMessage("An Alias Specified Has Already Been Used For Another Site", MessageType.Warning);
AddModuleMessage(Localizer["An Alias Specified Has Already Been Used For Another Site"], MessageType.Warning);
}
}
else
{
AddModuleMessage("You Must Provide A Site Name, Alias, And Default Theme/Container", MessageType.Warning);
AddModuleMessage(Localizer["You Must Provide A Site Name, Alias, And Default Theme/Container"], MessageType.Warning);
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Saving Site {SiteId} {Error}", PageState.Site.SiteId, ex.Message);
AddModuleMessage("Error Saving Site", MessageType.Error);
AddModuleMessage(Localizer["Error Saving Site"], MessageType.Error);
}
}
private async Task SendEmail()
{
if (_smtphost != "" && _smtpport != "" && _smtpsender != "")
{
try
{
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
SettingService.SetSetting(settings, "SMTPHost", _smtphost);
SettingService.SetSetting(settings, "SMTPPort", _smtpport);
SettingService.SetSetting(settings, "SMTPSSL", _smtpssl);
SettingService.SetSetting(settings, "SMTPUsername", _smtpusername);
SettingService.SetSetting(settings, "SMTPPassword", _smtppassword);
SettingService.SetSetting(settings, "SMTPSender", _smtpsender);
await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId);
await logger.LogInformation("Site SMTP Settings Saved");
await NotificationService.AddNotificationAsync(new Notification(PageState.Site.SiteId, PageState.User.DisplayName, PageState.User.Email, PageState.User.DisplayName, PageState.User.Email, PageState.Site.Name + " SMTP Configuration Test", "SMTP Server Is Configured Correctly."));
AddModuleMessage(Localizer["SMTP Settings Saved And A Message Has Been Sent To The Email Address Associated To Your User Account... Please Wait A Few Minutes For Delivery. If You Do Not Receive The Email Please Review The Notification Job In Scheduled Jobs For Any Log Details."], MessageType.Info);
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Testing SMTP Configuration");
AddModuleMessage(Localizer["Error Testing SMTP Configuration"], MessageType.Error);
}
}
else
{
AddModuleMessage(Localizer["You Must Specify The SMTP Host, Port, And Sender"], MessageType.Warning);
}
}
}

View File

@ -1,4 +1,4 @@
@namespace Oqtane.Modules.Admin.Sites
@namespace Oqtane.Modules.Admin.Sites
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject ITenantService TenantService
@ -8,17 +8,18 @@
@inject ISiteTemplateService SiteTemplateService
@inject IUserService UserService
@inject IInstallationService InstallationService
@inject IStringLocalizer<Add> Localizer
@if (_tenants == null)
{
<p><em>Loading...</em></p>
<p><em>@Localizer["Loading..."]</em></p>
}
else
{
<table class="table table-borderless">
<tr>
<td>
<Label For="name" HelpText="Enter the name of the site">Site Name: </Label>
<Label For="name" HelpText="Enter the name of the site" ResourceKey="Name">Site Name: </Label>
</td>
<td>
<input id="name" class="form-control" @bind="@_name" />
@ -26,7 +27,7 @@ else
</tr>
<tr>
<td>
<Label For="alias" HelpText="Enter the alias for the server">Aliases: </Label>
<Label For="alias" HelpText="Enter the alias for the server" ResourceKey="Aliases">Aliases: </Label>
</td>
<td>
<textarea id="alias" class="form-control" @bind="@_urls" rows="3"></textarea>
@ -34,11 +35,11 @@ else
</tr>
<tr>
<td>
<Label For="defaultTheme" HelpText="Select the default theme for the website">Default Theme: </Label>
<Label For="defaultTheme" HelpText="Select the default theme for the website" ResourceKey="DefaultTheme">Default Theme: </Label>
</td>
<td>
<select id="defaultTheme" class="form-control" @onchange="(e => ThemeChanged(e))">
<option value="-">&lt;Select Theme&gt;</option>
<option value="-">&lt;@Localizer["Select Theme"]&gt;</option>
@foreach (var theme in _themes)
{
<option value="@theme.TypeName">@theme.Name</option>
@ -46,30 +47,13 @@ else
</select>
</td>
</tr>
@if (_layouts.Count > 0)
{
<tr>
<td>
<Label For="defaultLayout" HelpText="Select the default layout for the site">Default Layout: </Label>
</td>
<td>
<select id="defaultLayout" class="form-control" @bind="@_layouttype">
<option value="-">&lt;Select Layout&gt;</option>
@foreach (var layout in _layouts)
{
<option value="@(layout.TypeName)">@(layout.Name)</option>
}
</select>
</td>
</tr>
}
<tr>
<td>
<Label For="defaultContainer" HelpText="Select the default container for the site">Default Container: </Label>
<Label For="defaultContainer" HelpText="Select the default container for the site" ResourceKey="DefaultContainer">Default Container: </Label>
</td>
<td>
<select id="defaultContainer" class="form-control" @bind="@_containertype">
<option value="-">&lt;Select Container&gt;</option>
<option value="-">&lt;@Localizer["Select Container"]&gt;</option>
@foreach (var container in _containers)
{
<option value="@container.TypeName">@container.Name</option>
@ -79,11 +63,26 @@ else
</tr>
<tr>
<td>
<Label For="siteTemplate" HelpText="Select the site template">Site Template: </Label>
<Label For="adminContainer" HelpText="Select the admin container for the site" ResourceKey="AdminContainer">Admin Container: </Label>
</td>
<td>
<select id="adminContainer" class="form-control" @bind="@_admincontainertype">
<option value="-">&lt;@Localizer["Select Container"]&gt;</option>
<option value="">&lt;@Localizer["Default Admin Container"]&gt;</option>
@foreach (var container in _containers)
{
<option value="@container.TypeName">@container.Name</option>
}
</select>
</td>
</tr>
<tr>
<td>
<Label For="siteTemplate" HelpText="Select the site template" ResourceKey="SiteTemplate">Site Template: </Label>
</td>
<td>
<select id="siteTemplate" class="form-control" @bind="@_sitetemplatetype">
<option value="-">&lt;Select Site Template&gt;</option>
<option value="-">&lt;@Localizer["Select Site Template"]&gt;</option>
@foreach (SiteTemplate siteTemplate in _siteTemplates)
{
<option value="@siteTemplate.TypeName">@siteTemplate.Name</option>
@ -93,12 +92,12 @@ else
</tr>
<tr>
<td>
<Label For="tenant" HelpText="Select the tenant for the site">Tenant: </Label>
<Label For="tenant" HelpText="Select the tenant for the site" ResourceKey="Tenant">Tenant: </Label>
</td>
<td>
<select id="tenant" class="form-control" @onchange="(e => TenantChanged(e))">
<option value="-">&lt;Select Tenant&gt;</option>
<option value="+">&lt;Create New Tenant&gt;</option>
<option value="-">&lt;@Localizer["Select Tenant"]&gt;</option>
<option value="+">&lt;@Localizer["Create New Tenant"]&gt;</option>
@foreach (Tenant tenant in _tenants)
{
<option value="@tenant.TenantId">@tenant.Name</option>
@ -115,7 +114,7 @@ else
</tr>
<tr>
<td>
<Label For="name" HelpText="Enter the name for the tenant">Tenant Name: </Label>
<Label For="name" HelpText="Enter the name for the tenant" ResourceKey="TenantName">Tenant Name: </Label>
</td>
<td>
<input id="name" class="form-control" @bind="@_tenantname" />
@ -123,18 +122,18 @@ else
</tr>
<tr>
<td>
<Label For="databaseType" HelpText="Select the database type for the tenant">Database Type: </Label>
<Label For="databaseType" HelpText="Select the database type for the tenant" ResourceKey="DatabaseType">Database Type: </Label>
</td>
<td>
<select id="databaseType" class="custom-select" @bind="@_databasetype">
<option value="LocalDB">Local Database</option>
<option value="SQLServer">SQL Server</option>
<option value="LocalDB">@Localizer["Local Database"]</option>
<option value="SQLServer">@Localizer["SQL Server"]</option>
</select>
</td>
</tr>
<tr>
<td>
<Label For="server" HelpText="Enter the server for the tenant">Server: </Label>
<Label For="server" HelpText="Enter the server for the tenant" ResourceKey="DatabaseServer">Server: </Label>
</td>
<td>
<input id="server" type="text" class="form-control" @bind="@_server" />
@ -142,7 +141,7 @@ else
</tr>
<tr>
<td>
<Label For="database" HelpText="Enter the database for the tenant">Database: </Label>
<Label For="database" HelpText="Enter the database for the tenant" ResourceKey="Database">Database: </Label>
</td>
<td>
<input id="database" type="text" class="form-control" @bind="@_database" />
@ -150,12 +149,12 @@ else
</tr>
<tr>
<td>
<Label For="integratedSecurity" HelpText="Select if you want integrated security or not">Integrated Security: </Label>
<Label For="integratedSecurity" HelpText="Select if you want integrated security or not" ResourceKey="IntegratedSecurity">Integrated Security: </Label>
</td>
<td>
<select id="integratedSecurity" class="custom-select" @onchange="SetIntegratedSecurity">
<option value="true" selected>True</option>
<option value="false">False</option>
<option value="true" selected>@Localizer["True"]</option>
<option value="false">@Localizer["False"]</option>
</select>
</td>
</tr>
@ -163,7 +162,7 @@ else
{
<tr>
<td>
<Label For="username" HelpText="Enter the username for the integrated security">Database Username: </Label>
<Label For="username" HelpText="Enter the username for the integrated security" ResourceKey="DatabaseUsername">Database Username: </Label>
</td>
<td>
<input id="username" type="text" class="form-control" @bind="@_username" />
@ -171,7 +170,7 @@ else
</tr>
<tr>
<td>
<Label For="password" HelpText="Enter the password for the integrated security">Database Password: </Label>
<Label For="password" HelpText="Enter the password for the integrated security" ResourceKey="DatabasePassword">Database Password: </Label>
</td>
<td>
<input id="password" type="password" class="form-control" @bind="@_password" />
@ -180,7 +179,7 @@ else
}
<tr>
<td>
<Label For="hostUsername" HelpText="Enter the username of the host for this site">Host Username:</Label>
<Label For="hostUsername" HelpText="Enter the username of the host for this site" ResourceKey="HostUsername">Host Username:</Label>
</td>
<td>
<input id="hostUsername" class="form-control" @bind="@_hostusername" readonly />
@ -188,7 +187,7 @@ else
</tr>
<tr>
<td>
<Label For="hostPassword" HelpText="Enter the password for the host of this site">Host Password:</Label>
<Label For="hostPassword" HelpText="Enter the password for the host of this site" ResourceKey="HostPassword">Host Password:</Label>
</td>
<td>
<input id="hostPassword" type="password" class="form-control" @bind="@_hostpassword" />
@ -196,14 +195,13 @@ else
</tr>
}
</table>
<button type="button" class="btn btn-success" @onclick="SaveSite">Save</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink>
<button type="button" class="btn btn-success" @onclick="SaveSite">@Localizer["Save"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@Localizer["Cancel"]</NavLink>
}
@code {
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;
@ -216,14 +214,14 @@ else
private string _username = string.Empty;
private string _password = string.Empty;
private bool _integratedsecurity = true;
private string _hostusername = Constants.HostUser;
private string _hostusername = UserNames.Host;
private string _hostpassword = string.Empty;
private string _name = string.Empty;
private string _urls = string.Empty;
private string _themetype = "-";
private string _layouttype = "-";
private string _containertype = "-";
private string _admincontainertype = "";
private string _sitetemplatetype = "-";
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
@ -267,28 +265,26 @@ else
_themetype = (string)e.Value;
if (_themetype != "-")
{
_layouts = ThemeService.GetLayoutControls(_themeList, _themetype);
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
}
else
{
_layouts = new List<ThemeControl>();
_containers = new List<ThemeControl>();
}
_layouttype = "-";
_containertype = "-";
_admincontainertype = "";
StateHasChanged();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading Pane Layouts For Theme {ThemeType} {Error}", _themetype, ex.Message);
AddModuleMessage("Error Loading Pane Layouts For Theme", MessageType.Error);
await logger.LogError(ex, "Error Loading Containers For Theme {ThemeType} {Error}", _themetype, ex.Message);
AddModuleMessage(Localizer["Error Loading Containers For Theme"], MessageType.Error);
}
}
private async Task SaveSite()
{
if (_tenantid != "-" && _name != string.Empty && _urls != string.Empty && _themetype != "-" && (_layouts.Count == 0 || _layouttype != "-") && _containertype != "-" && _sitetemplatetype != "-")
if (_tenantid != "-" && _name != string.Empty && _urls != string.Empty && _themetype != "-" && _containertype != "-" && _sitetemplatetype != "-")
{
var duplicates = new List<string>();
var aliases = await AliasService.GetAliasesAsync();
@ -311,7 +307,7 @@ else
// validate host credentials
var user = new User();
user.SiteId = PageState.Site.SiteId;
user.Username = Constants.HostUser;
user.Username = UserNames.Host;
user.Password = _hostpassword;
user = await UserService.LoginUserAsync(user, false, false);
if (user.IsAuthenticated)
@ -346,17 +342,17 @@ else
}
else
{
AddModuleMessage("You Must Specify A Server And Database", MessageType.Error);
AddModuleMessage(Localizer["You Must Specify A Server And Database"], MessageType.Error);
}
}
else
{
AddModuleMessage("Invalid Host Password", MessageType.Error);
AddModuleMessage(Localizer["Invalid Host Password"], MessageType.Error);
}
}
else
{
AddModuleMessage("Tenant Name Is Missing Or Already Exists", MessageType.Error);
AddModuleMessage(Localizer["Tenant Name Is Missing Or Already Exists"], MessageType.Error);
}
}
else
@ -375,8 +371,8 @@ else
config.SiteName = _name;
config.Aliases = _urls.Replace("\n", ",");
config.DefaultTheme = _themetype;
config.DefaultLayout = _layouttype;
config.DefaultContainer = _containertype;
config.DefaultAdminContainer = _admincontainertype;
config.SiteTemplate = _sitetemplatetype;
ShowProgressIndicator();
@ -397,12 +393,12 @@ else
}
else
{
AddModuleMessage(string.Join(", ", duplicates.ToArray()) + " Already Used For Another Site", MessageType.Warning);
AddModuleMessage(Localizer["{0} Already Used For Another Site", string.Join(", ", duplicates.ToArray())], MessageType.Warning);
}
}
else
{
AddModuleMessage("You Must Provide A Tenant, Site Name, Alias, Default Theme/Container, And Site Template", MessageType.Warning);
AddModuleMessage(Localizer["You Must Provide A Tenant, Site Name, Alias, Default Theme/Container, And Site Template"], MessageType.Warning);
}
}
}

View File

@ -1,17 +1,18 @@
@namespace Oqtane.Modules.Admin.Sites
@namespace Oqtane.Modules.Admin.Sites
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject ISiteService SiteService
@inject ITenantService TenantService
@inject IAliasService AliasService
@inject IThemeService ThemeService
@inject IStringLocalizer<Edit> Localizer
@if (_themes != null)
@if (_initialized)
{
<table class="table table-borderless">
<tr>
<td>
<Label For="name" HelpText="Enter the name of the site">Name: </Label>
<Label For="name" HelpText="Enter the name of the site" ResourceKey="Name">Name: </Label>
</td>
<td>
<input id="name" class="form-control" @bind="@_name" />
@ -19,15 +20,7 @@
</tr>
<tr>
<td>
<Label For="tenant" HelpText="Enter the tenant for the site">Tenant: </Label>
</td>
<td>
<input id="tenant" class="form-control" @bind="@_tenant" readonly />
</td>
</tr>
<tr>
<td>
<Label For="alias" HelpText="Enter the alias for the server">Aliases: </Label>
<Label For="alias" HelpText="Enter the alias for the server" ResourceKey="Aliases">Aliases: </Label>
</td>
<td>
<textarea id="alias" class="form-control" @bind="@_urls" rows="3" />
@ -35,49 +28,25 @@
</tr>
<tr>
<td>
<Label For="defaultTheme" HelpText="Select the default theme for the website">Default Theme: </Label>
<Label For="defaultTheme" HelpText="Select the default theme for the website" ResourceKey="DefaultTheme">Default Theme: </Label>
</td>
<td>
<select id="defaultTheme" class="form-control" @onchange="(e => ThemeChanged(e))">
<option value="-">&lt;Select Theme&gt;</option>
<select id="defaultTheme" class="form-control" value="@_themetype" @onchange="(e => ThemeChanged(e))">
<option value="-">&lt;@Localizer["Select Theme"]&gt;</option>
@foreach (var theme in _themes)
{
if (theme.TypeName == _themetype)
{
<option value="@theme.TypeName" selected>@theme.Name</option>
}
else
{
<option value="@theme.TypeName">@theme.Name</option>
}
<option value="@theme.TypeName">@theme.Name</option>
}
</select>
</td>
</tr>
@if (_layouts.Count > 0)
{
<tr>
<td>
<Label For="defaultLayout" HelpText="Select the default layout for the site">Default Layout: </Label>
</td>
<td>
<select id="defaultLayout" class="form-control" @bind="@_layouttype">
<option value="-">&lt;Select Layout&gt;</option>
@foreach (var layout in _layouts)
{
<option value="@(layout.TypeName)">@(layout.Name)</option>
}
</select>
</td>
</tr>
}
<tr>
<td>
<Label For="defaultContainer" HelpText="Select the default container for the site">Default Container: </Label>
<Label For="defaultContainer" HelpText="Select the default container for the site" ResourceKey="DefaultContainer">Default Container: </Label>
</td>
<td>
<select id="defaultIdea" class="form-control" @bind="@_containertype">
<option value="-">&lt;Select Container&gt;</option>
<option value="-">&lt;@Localizer["Select Container"]&gt;</option>
@foreach (var container in _containers)
{
<option value="@container.TypeName">@container.Name</option>
@ -87,38 +56,68 @@
</tr>
<tr>
<td>
<Label For="isDeleted" HelpText="Has this site been deleted?">Is Deleted? </Label>
<Label For="defaultAdminContainer" HelpText="Select the default admin container for the site" ResourceKey="DefaultAdminContainer">Default Admin Container: </Label>
</td>
<td>
<select id="defaultAdminContainer" class="form-control" @bind="@_admincontainertype">
<option value="-">&lt;@Localizer["Select Container"]&gt;</option>
<option value="">&lt;@Localizer["Default Admin Container"]&gt;</option>
@foreach (var container in _containers)
{
<option value="@container.TypeName">@container.Name</option>
}
</select>
</td>
</tr>
<tr>
<td>
<Label For="isDeleted" HelpText="Has this site been deleted?" ResourceKey="IsDeleted">Is Deleted? </Label>
</td>
<td>
<select id="isDeleted" class="form-control" @bind="@_isdeleted">
<option value="True">Yes</option>
<option value="False">No</option>
<option value="True">@Localizer["Yes"]</option>
<option value="False">@Localizer["No"]</option>
</select>
</td>
</tr>
<tr>
<td>
<Label For="tenant" HelpText="The tenant for the site" ResourceKey="Tenant">Tenant: </Label>
</td>
<td>
<input id="tenant" class="form-control" @bind="@_tenant" readonly />
</td>
</tr>
<tr>
<td>
<Label For="connectionstring" HelpText="The database connection string" ResourceKey="ConnectionString">Connection String: </Label>
</td>
<td>
<textarea id="connectionstring" class="form-control" @bind="@_connectionstring" rows="3" readonly></textarea>
</td>
</tr>
</table>
<br />
<button type="button" class="btn btn-success" @onclick="SaveSite">Save</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink>
<button type="button" class="btn btn-success" @onclick="SaveSite">@Localizer["Save"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@Localizer["Cancel"]</NavLink>
<br />
<br />
<AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon" DeletedBy="@_deletedby" DeletedOn="@_deletedon"></AuditInfo>
}
@code {
private bool _initialized = false;
private List<Theme> _themeList;
private List<ThemeControl> _themes = new List<ThemeControl>();
private List<ThemeControl> _layouts = new List<ThemeControl>();
private List<ThemeControl> _containers = new List<ThemeControl>();
private Alias _alias;
private string _name = string.Empty;
private List<Tenant> _tenantList;
private string _tenant = string.Empty;
private List<Alias> _aliasList;
private string _urls = string.Empty;
private string _themetype;
private string _layouttype;
private string _containertype;
private string _containertype = "-";
private string _admincontainertype = "-";
private string _createdby;
private DateTime _createdon;
private string _modifiedby;
@ -126,6 +125,8 @@
private string _deletedby;
private DateTime? _deletedon;
private string _isdeleted;
private string _tenant = string.Empty;
private string _connectionstring = string.Empty;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
@ -142,8 +143,6 @@
if (site != null)
{
_name = site.Name;
_tenantList = await TenantService.GetTenantsAsync();
_tenant = _tenantList.Find(item => item.TenantId == site.TenantId).Name;
foreach (Alias alias in _aliasList.Where(item => item.SiteId == site.SiteId && item.TenantId == site.TenantId).ToList())
{
@ -152,10 +151,9 @@
_themes = ThemeService.GetThemeControls(_themeList);
_themetype = site.DefaultThemeType;
_layouts = ThemeService.GetLayoutControls(_themeList, _themetype);
_layouttype = site.DefaultLayoutType;
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
_containertype = site.DefaultContainerType;
_admincontainertype = site.AdminContainerType;
_createdby = site.CreatedBy;
_createdon = site.CreatedOn;
_modifiedby = site.ModifiedBy;
@ -163,6 +161,16 @@
_deletedby = site.DeletedBy;
_deletedon = site.DeletedOn;
_isdeleted = site.IsDeleted.ToString();
List<Tenant> tenants = await TenantService.GetTenantsAsync();
Tenant tenant = tenants.Find(item => item.TenantId == site.TenantId);
if (tenant != null)
{
_tenant = tenant.Name;
_connectionstring = tenant.DBConnectionString;
}
_initialized = true;
}
}
catch (Exception ex)
@ -179,22 +187,20 @@
_themetype = (string)e.Value;
if (_themetype != "-")
{
_layouts = ThemeService.GetLayoutControls(_themeList, _themetype);
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
}
else
{
_layouts = new List<ThemeControl>();
_containers = new List<ThemeControl>();
}
_layouttype = "-";
_containertype = "-";
_admincontainertype = "";
StateHasChanged();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading Pane Layouts For Theme {ThemeType} {Error}", _themetype, ex.Message);
AddModuleMessage("Error Loading Pane Layouts For Theme", MessageType.Error);
AddModuleMessage(Localizer["Error Loading Pane Layouts For Theme"], MessageType.Error);
}
}
@ -202,7 +208,7 @@
{
try
{
if (_name != string.Empty && _urls != string.Empty && _themetype != "-" && (_layouts.Count == 0 || _layouttype != "-") && _containertype != "-")
if (_name != string.Empty && _urls != string.Empty && _themetype != "-" && _containertype != "-")
{
var unique = true;
foreach (string name in _urls.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
@ -222,8 +228,8 @@
site.Name = _name;
site.LogoFileId = null;
site.DefaultThemeType = _themetype;
site.DefaultLayoutType = _layouttype ?? string.Empty;
site.DefaultContainerType = _containertype;
site.AdminContainerType = _admincontainertype;
site.IsDeleted = (_isdeleted == null || Boolean.Parse(_isdeleted));
site = await SiteService.UpdateSiteAsync(site);
@ -253,25 +259,25 @@
}
}
await Log(_alias, LogLevel.Information,PermissionNames.Edit, null, "Site Saved {Site}", site);
await Log(_alias, LogLevel.Information, PermissionNames.Edit, null, "Site Saved {Site}", site);
NavigationManager.NavigateTo(NavigateUrl());
}
}
else
{
AddModuleMessage("An Alias Specified Has Already Been Used For Another Site", MessageType.Warning);
AddModuleMessage(Localizer["An Alias Specified Has Already Been Used For Another Site"], MessageType.Warning);
}
}
else
{
AddModuleMessage("You Must Provide A Site Name, Alias, And Default Theme/Container", MessageType.Warning);
AddModuleMessage(Localizer["You Must Provide A Site Name, Alias, And Default Theme/Container"], MessageType.Warning);
}
}
catch (Exception ex)
{
await Log(_alias, LogLevel.Error, string.Empty, ex, "Error Saving Site {SiteId} {Error}", _alias.SiteId, ex.Message);
AddModuleMessage("Error Saving Site", MessageType.Error);
AddModuleMessage(Localizer["Error Saving Site"], MessageType.Error);
}
}
}
}

View File

@ -1,8 +1,9 @@
@namespace Oqtane.Modules.Admin.Sites
@namespace Oqtane.Modules.Admin.Sites
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IAliasService AliasService
@inject ISiteService SiteService
@inject IStringLocalizer<Index> Localizer
@if (_sites == null)
{
@ -10,18 +11,18 @@
}
else
{
<ActionLink Action="Add" Text="Add Site" />
<ActionLink Action="Add" Text="Add Site" ResourceKey="AddSite" />
<Pager Items="@_sites">
<Header>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>Name</th>
<th>@Localizer["Name"]</th>
</Header>
<Row>
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.AliasId.ToString())" /></td>
<td><ActionDialog Header="Delete Site" Message="@("Are You Sure You Wish To Delete The " + context.Name + " Site?")" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteSite(context))" /></td>
<td><a href="@(_scheme + context.Name)">@context.Name</a></td>
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.AliasId.ToString())" ResourceKey="EditSite" /></td>
<td><ActionDialog Header="Delete Site" Message="@Localizer["Are You Sure You Wish To Delete The {0} Site?", context.Name]" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteSite(context))" ResourceKey="DeleteSite" /></td>
<td><a href="@(_scheme + context.Name +"?reload")">@context.Name</a></td>
</Row>
</Pager>
}
@ -57,24 +58,24 @@ else
SiteService.SetAlias(alias);
await SiteService.DeleteSiteAsync(alias.SiteId);
await Log(alias, LogLevel.Information, "", null, "Site Deleted {SiteId}", alias.SiteId);
var aliases = await AliasService.GetAliasesAsync();
foreach (Alias a in aliases.Where(item => item.SiteId == alias.SiteId && item.TenantId == alias.TenantId))
{
await AliasService.DeleteAliasAsync(a.AliasId);
}
NavigationManager.NavigateTo(NavigateUrl());
}
else
{
AddModuleMessage("You Can Not Delete The Current Site", MessageType.Warning);
AddModuleMessage(Localizer["You Can Not Delete The Current Site"], MessageType.Warning);
}
}
catch (Exception ex)
{
await Log(alias, LogLevel.Error, "", ex, "Error Deleting Site {SiteId} {Error}", alias.SiteId, ex.Message);
AddModuleMessage("Error Deleting Site", MessageType.Error);
AddModuleMessage(Localizer["Error Deleting Site"], MessageType.Error);
}
}
}

View File

@ -1,8 +1,9 @@
@namespace Oqtane.Modules.Admin.Sql
@namespace Oqtane.Modules.Admin.Sql
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject ITenantService TenantService
@inject ISqlService SqlService
@inject IStringLocalizer<Index> Localizer
@if (_tenants == null)
{
@ -13,11 +14,11 @@ else
<table class="table table-borderless">
<tr>
<td>
<Label For="tenant" HelpText="Select the tenant for the SQL server">Tenant: </Label>
<Label For="tenant" HelpText="Select the tenant for the SQL server" ResourceKey="Tenant">Tenant: </Label>
</td>
<td>
<select id="teneant" class="form-control" @bind="_tenantid">
<option value="-1">&lt;Select Tenant&gt;</option>
<option value="-1">&lt;@Localizer["Select Tenant"]&gt;</option>
@foreach (Tenant tenant in _tenants)
{
<option value="@tenant.TenantId">@tenant.Name</option>
@ -27,15 +28,16 @@ else
</tr>
<tr>
<td>
<Label For="sqlQeury" HelpText="Enter the query for the SQL server">SQL Query: </Label>
<Label For="sqlQeury" HelpText="Enter the query for the SQL server" ResourceKey="SqlQuery">SQL Query: </Label>
</td>
<td>
<textarea id="sqlQeury" class="form-control" @bind="@_sql" rows="5"></textarea>
</td>
</tr>
</table>
<button type="button" class="btn btn-success" @onclick="Execute">Execute</button>
<br /><br />
<button type="button" class="btn btn-success" @onclick="Execute">@Localizer["Execute"]</button>
<br />
<br />
@if (!string.IsNullOrEmpty(_results))
{
@((MarkupString)_results)
@ -65,7 +67,7 @@ else
}
else
{
AddModuleMessage("You Must Select A Tenant And Provide A SQL Query", MessageType.Warning);
AddModuleMessage(Localizer["You Must Select A Tenant And Provide A SQL Query"], MessageType.Warning);
}
}
@ -78,34 +80,34 @@ else
{
table = "<div class=\"table-responsive\">";
table += "<table class=\"table table-bordered\"><thead><tr>";
foreach (KeyValuePair<string, string> kvp in item)
{
table += "<th scope=\"col\">" + kvp.Key + "</th>";
}
table += "</tr></thead><tbody>";
}
table += "<tr>";
foreach (KeyValuePair<string, string> kvp in item)
{
table += "<td>" + kvp.Value + "</td>";
}
table += "</tr>";
}
if (table != string.Empty)
{
table += "</tbody></table></div>";
}
else
{
table = "No Results Returned";
table = Localizer["No Results Returned"];
}
return table;
}
}

View File

@ -1,11 +1,13 @@
@namespace Oqtane.Modules.Admin.SystemInfo
@namespace Oqtane.Modules.Admin.SystemInfo
@inherits ModuleBase
@inject ISystemService SystemService
@inject IInstallationService InstallationService
@inject IStringLocalizer<Index> Localizer
<table class="table table-borderless">
<tr>
<td>
<Label For="version" HelpText="Framework Version">Framework Version: </Label>
<Label For="version" HelpText="Framework Version" ResourceKey="FrameworkVersion">Framework Version: </Label>
</td>
<td>
<input id="version" class="form-control" @bind="@_version" readonly />
@ -13,7 +15,7 @@
</tr>
<tr>
<td>
<Label For="runtime" HelpText="Blazor Runtime (Server or WebAssembly)">Blazor Runtime: </Label>
<Label For="runtime" HelpText="Blazor Runtime (Server or WebAssembly)" ResourceKey="BlazorRunime">Blazor Runtime: </Label>
</td>
<td>
<input id="runtime" class="form-control" @bind="@_runtime" readonly />
@ -21,7 +23,7 @@
</tr>
<tr>
<td>
<Label For="clrversion" HelpText="Common Language Runtime Version">CLR Version: </Label>
<Label For="clrversion" HelpText="Common Language Runtime Version" ResourceKey="ClrVerion">CLR Version: </Label>
</td>
<td>
<input id="clrversion" class="form-control" @bind="@_clrversion" readonly />
@ -29,7 +31,7 @@
</tr>
<tr>
<td>
<Label For="osversion" HelpText="Operating System Version">OS Version: </Label>
<Label For="osversion" HelpText="Operating System Version" ResourceKey="OsVersion">OS Version: </Label>
</td>
<td>
<input id="osversion" class="form-control" @bind="@_osversion" readonly />
@ -37,7 +39,7 @@
</tr>
<tr>
<td>
<Label For="serverpath" HelpText="Server Path">Server Path: </Label>
<Label For="serverpath" HelpText="Server Path" ResourceKey="ServerPath">Server Path: </Label>
</td>
<td>
<input id="serverpath" class="form-control" @bind="@_serverpath" readonly />
@ -45,14 +47,15 @@
</tr>
<tr>
<td>
<Label For="servertime" HelpText="Server Time">Server Time: </Label>
<Label For="servertime" HelpText="Server Time" ResourceKey="ServerTime">Server Time: </Label>
</td>
<td>
<input id="servertime" class="form-control" @bind="@_servertime" readonly />
</td>
</tr>
</table>
<a class="btn btn-primary" href="swagger/index.html" target="_new">Access Framework API</a>
<a class="btn btn-primary" href="swagger/index.html" target="_new">@Localizer["Access Framework API"]</a>&nbsp;
<ActionDialog Header="Restart Application" Message="Are You Sure You Wish To Restart The Application?" Action="Restart Application" Security="SecurityAccessLevel.Host" Class="btn btn-danger" OnClick="@(async () => await RestartApplication())" ResourceKey="RestartApplication" />
@code {
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
@ -78,4 +81,19 @@
_servertime = systeminfo["servertime"];
}
}
}
private async Task RestartApplication()
{
try
{
ShowProgressIndicator();
var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(""), 10);
await InstallationService.RestartAsync();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Restarting Application");
}
}
}

View File

@ -1,85 +0,0 @@
@namespace Oqtane.Modules.Admin.Tenants
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject ITenantService TenantService
<table class="table table-borderless">
<tr>
<td>
<Label For="name" HelpText="The name of the tenant">Name: </Label>
</td>
<td>
@if (name == Constants.MasterTenant)
{
<input id="name" class="form-control" @bind="@name" readonly />
}
else
{
<input id="name" class="form-control" @bind="@name" />
}
</td>
</tr>
<tr>
<td>
<Label For="connectionstring" HelpText="The database connection string">Connection String: </Label>
</td>
<td>
<textarea id="connectionstring" class="form-control" @bind="@connectionstring" rows="3" readonly></textarea>
</td>
</tr>
</table>
<button type="button" class="btn btn-success" @onclick="SaveTenant">Save</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink>
@code {
private int tenantid;
private string name = string.Empty;
private string connectionstring = string.Empty;
private string schema = string.Empty;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
protected override async Task OnInitializedAsync()
{
try
{
tenantid = Int32.Parse(PageState.QueryString["id"]);
var tenant = await TenantService.GetTenantAsync(tenantid);
if (tenant != null)
{
name = tenant.Name;
connectionstring = tenant.DBConnectionString;
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading Tenant {TenantId} {Error}", tenantid, ex.Message);
AddModuleMessage("Error Loading Tenant", MessageType.Error);
}
}
private async Task SaveTenant()
{
try
{
connectionstring = connectionstring.Replace("\\\\", "\\");
var tenant = await TenantService.GetTenantAsync(tenantid);
if (tenant != null)
{
tenant.Name = name;
tenant.DBConnectionString = connectionstring;
await TenantService.UpdateTenantAsync(tenant);
await logger.LogInformation("Tenant Saved {TenantId}", tenantid);
NavigationManager.NavigateTo(NavigateUrl());
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Saving Tenant {TenantId} {Error}", tenantid, ex.Message);
AddModuleMessage("Error Saving Tenant", MessageType.Error);
}
}
}

View File

@ -1,67 +0,0 @@
@namespace Oqtane.Modules.Admin.Tenants
@inherits ModuleBase
@inject ITenantService TenantService
@inject IAliasService AliasService
@if (tenants == null)
{
<p><em>Loading...</em></p>
}
else
{
<Pager Items="@tenants">
<Header>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>Name</th>
</Header>
<Row>
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.TenantId.ToString())" /></td>
<td><ActionDialog Header="Delete Tenant" Message="@("Are You Sure You Wish To Delete The " + context.Name + " Tenant?")" Action="Delete" Security="SecurityAccessLevel.Host" Class="btn btn-danger" OnClick="@(async () => await DeleteTenant(context))" Disabled="@(context.Name == Constants.MasterTenant)" /></td>
<td>@context.Name</td>
</Row>
</Pager>
}
@code {
private List<Tenant> tenants;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
protected override async Task OnParametersSetAsync()
{
tenants = await TenantService.GetTenantsAsync();
}
private async Task DeleteTenant(Tenant Tenant)
{
try
{
string message = string.Empty;
var aliases = await AliasService.GetAliasesAsync();
foreach (var alias in aliases)
{
if (alias.TenantId == Tenant.TenantId)
{
message += ", " + alias.Name;
}
}
if (string.IsNullOrEmpty(message))
{
await TenantService.DeleteTenantAsync(Tenant.TenantId);
await logger.LogInformation("Tenant Deleted {Tenant}", Tenant);
StateHasChanged();
}
else
{
AddModuleMessage("Tenant Cannot Be Deleted Until The Following Sites Are Deleted: " + message.Substring(2), MessageType.Warning);
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Deleting Tenant {Tenant} {Error}", Tenant, ex.Message);
AddModuleMessage("Error Deleting Tenant", MessageType.Error);
}
}
}

View File

@ -1,38 +1,39 @@
@namespace Oqtane.Modules.Admin.Themes
@namespace Oqtane.Modules.Admin.Themes
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IFileService FileService
@inject IThemeService ThemeService
@inject IPackageService PackageService
@inject IStringLocalizer<Add> Localizer
@if (_packages != null)
{
<TabStrip>
@if (_packages.Count > 0)
{
<TabPanel Name="Download">
<TabPanel Name="Download" ResourceKey="Download">
<ModuleMessage Type="MessageType.Info" Message="Download one or more themes from the list below. Once you are ready click Install to complete the installation."></ModuleMessage>
<Pager Items="@_packages">
<Header>
<th>Name</th>
<th>Version</th>
<th>@Localizer["Name"]</th>
<th>@Localizer["Version"]</th>
<th style="width: 1px;"></th>
</Header>
<Row>
<td>@context.Name</td>
<td>@context.Version</td>
<td>
<button type="button" class="btn btn-primary" @onclick=@(async () => await DownloadTheme(context.PackageId, context.Version))>Download</button>
<button type="button" class="btn btn-primary" @onclick=@(async () => await DownloadTheme(context.PackageId, context.Version))>@Localizer["Download"]</button>
</td>
</Row>
</Pager>
</TabPanel>
}
<TabPanel Name="Upload">
<TabPanel Name="Upload" ResourceKey="Upload">
<table class="table table-borderless">
<tr>
<td>
<Label HelpText="Upload one or more theme packages. Once they are uploaded click Install to complete the installation.">Theme: </Label>
<Label HelpText="Upload one or more theme packages. Once they are uploaded click Install to complete the installation." ResourceKey="Theme">Theme: </Label>
</td>
<td>
<FileManager Filter="nupkg" ShowFiles="false" Folder="Themes" UploadMultiple="@true" />
@ -42,8 +43,8 @@
</TabPanel>
</TabStrip>
<button type="button" class="btn btn-success" @onclick="InstallThemes">Install</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink>
<button type="button" class="btn btn-success" @onclick="InstallThemes">@Localizer["Install"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@Localizer["Cancel"]</NavLink>
}
@code {
@ -69,7 +70,7 @@
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading Packages {Error}", ex.Message);
AddModuleMessage("Error Loading Packages", MessageType.Error);
AddModuleMessage(Localizer["Error Loading Packages"], MessageType.Error);
}
}
@ -77,14 +78,12 @@
{
try
{
ShowProgressIndicator();
var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 3);
await ThemeService.InstallThemesAsync();
AddModuleMessage(Localizer["Theme Installed Successfully. You Must <a href=\"{0}\">Restart</a> Your Application To Apply These Changes.", NavigateUrl("admin/system")], MessageType.Success);
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Installating Theme");
await logger.LogError(ex, "Error Installing Theme");
}
}
@ -94,13 +93,13 @@
{
await PackageService.DownloadPackageAsync(packageid, version, "Themes");
await logger.LogInformation("Theme {ThemeName} {Version} Downloaded Successfully", packageid, version);
AddModuleMessage("Themes Downloaded Successfully. Click Install To Complete Installation.", MessageType.Success);
AddModuleMessage(Localizer["Themes Downloaded Successfully. Click Install To Complete Installation."], MessageType.Success);
StateHasChanged();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Downloading Module {ThemeName} {Version}", packageid, version);
AddModuleMessage("Error Downloading Theme", MessageType.Error);
AddModuleMessage(Localizer["Error Downloading Theme"], MessageType.Error);
}
}
}

View File

@ -0,0 +1,151 @@
@namespace Oqtane.Modules.Admin.Themes
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IThemeService ThemeService
@inject IModuleService ModuleService
@inject IPageModuleService PageModuleService
@inject ISystemService SystemService
@inject ISettingService SettingService
@inject IStringLocalizer<Index> Localizer
@using System.Text.RegularExpressions
@using System.IO;
@if (_systeminfo != null && _templates != null)
{
<table class="table table-borderless">
<tr>
<td>
<Label For="owner" HelpText="Enter the name of the organization who is developing this theme. It should not contain spaces or punctuation." ResourceKey="OwnerName">Owner Name: </Label>
</td>
<td>
<input id="owner" class="form-control" @bind="@_owner" />
</td>
</tr>
<tr>
<td>
<Label For="module" HelpText="Enter a name for this theme. It should not contain spaces or punctuation." ResourceKey="ThemeName">Theme Name: </Label>
</td>
<td>
<input id="module" class="form-control" @bind="@_theme" />
</td>
</tr>
<tr>
<td>
<Label For="template" HelpText="Select a theme template. Templates are located in the wwwroot/Themes/Templates folder on the server." ResourceKey="Template">Template: </Label>
</td>
<td>
<select id="template" class="form-control" @onchange="(e => TemplateChanged(e))">
<option value="-">&lt;@Localizer["Select Template"]&gt;</option>
@foreach (string template in _templates)
{
<option value="@template">@template</option>
}
</select>
</td>
</tr>
<tr>
<td>
<Label For="reference" HelpText="Select a framework reference version" ResourceKey="FrameworkReference">Framework Reference: </Label>
</td>
<td>
<select id="reference" class="form-control" @bind="@_reference">
@foreach (string version in Constants.ReleaseVersions.Split(','))
{
if (Version.Parse(version).CompareTo(Version.Parse("2.0.0")) >= 0)
{
<option value="@(version)">@(version)</option>
}
}
<option value="local">@Localizer["Local Version"]</option>
</select>
</td>
</tr>
@if (!string.IsNullOrEmpty(_location))
{
<tr>
<td>
<Label For="location" HelpText="Location where the theme will be created" ResourceKey="Location">Location: </Label>
</td>
<td>
<input id="module" class="form-control" @bind="@_location" readonly />
</td>
</tr>
}
</table>
<button type="button" class="btn btn-success" @onclick="CreateTheme">@Localizer["Create Theme"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@Localizer["Cancel"]</NavLink>
}
@code {
private string _owner = string.Empty;
private string _theme = string.Empty;
private string _template = "-";
private string _reference = Constants.Version;
private string _location = string.Empty;
private Dictionary<string, string> _systeminfo;
private List<string> _templates;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
protected override async Task OnParametersSetAsync()
{
try
{
_systeminfo = await SystemService.GetSystemInfoAsync();
_templates = await ThemeService.GetThemeTemplatesAsync();
AddModuleMessage(Localizer["Please Note That The Theme Creator Is Only Intended To Be Used In A Development Environment"], MessageType.Info);
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading Theme Creator");
}
}
private async Task CreateTheme()
{
try
{
if (IsValid(_owner) && IsValid(_theme) && _owner != _theme && _template != "-")
{
var theme = new Theme { Owner = _owner, Name = _theme, Template = _template, Version = _reference };
theme = await ThemeService.CreateThemeAsync(theme);
GetLocation();
AddModuleMessage(Localizer["The Source Code For Your Theme Has Been Created At The Location Specified Below And Must Be Compiled In Order To Make It Functional. Once It Has Been Compiled You Must <a href=\"{0}\">Restart</a> Your Application To Activate The Module.", NavigateUrl("admin/system")], MessageType.Success);
}
else
{
AddModuleMessage(Localizer["You Must Provide A Valid Owner Name And Theme Name ( ie. No Punctuation Or Spaces And The Values Cannot Be The Same ) And Choose A Template"], MessageType.Warning);
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Creating Theme");
}
}
private bool IsValid(string name)
{
// must contain letters, underscores and digits and first character must be letter or underscore
return !string.IsNullOrEmpty(name) && Regex.IsMatch(name, "^[A-Za-z_][A-Za-z0-9_]*$");
}
private void TemplateChanged(ChangeEventArgs e)
{
_template = (string)e.Value;
GetLocation();
}
private void GetLocation()
{
_location = string.Empty;
if (_template != "-" && _systeminfo != null && _systeminfo.ContainsKey("serverpath"))
{
string[] path = _systeminfo["serverpath"].Split(Path.DirectorySeparatorChar);
_location = string.Join(Path.DirectorySeparatorChar, path, 0, path.Length - 2) +
Path.DirectorySeparatorChar + _owner + "." + _theme;
}
StateHasChanged();
}
}

View File

@ -1,9 +1,10 @@
@namespace Oqtane.Modules.Admin.Themes
@namespace Oqtane.Modules.Admin.Themes
@using System.Net
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IThemeService ThemeService
@inject IPackageService PackageService
@inject IStringLocalizer<Index> Localizer
@if (_themes == null)
{
@ -12,21 +13,23 @@
else
{
<ActionLink Action="Add" Text="Install Theme" />
@((MarkupString)"&nbsp;")
<ActionLink Action="Create" Text="Create Theme" ResourceKey="CreateTheme" />
<Pager Items="@_themes">
<Header>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th scope="col">Name</th>
<th scope="col">Version</th>
<th scope="col">@Localizer["Name"]</th>
<th scope="col">@Localizer["Version"]</th>
<th>&nbsp;</th>
</Header>
<Row>
<td><ActionLink Action="View" Parameters="@($"name=" + WebUtility.UrlEncode(context.ThemeName))" /></td>
<td><ActionLink Action="View" Parameters="@($"name=" + WebUtility.UrlEncode(context.ThemeName))" ResourceKey="ViewTheme" /></td>
<td>
@if (context.AssemblyName != "Oqtane.Client")
{
<ActionDialog Header="Delete Theme" Message="@("Are You Sure You Wish To Delete The " + context.Name + " Theme?")" Action="Delete" Security="SecurityAccessLevel.Host" Class="btn btn-danger" OnClick="@(async () => await DeleteTheme(context))" />
<ActionDialog Header="Delete Theme" Message="@Localizer["Are You Sure You Wish To Delete The {0} Theme?", context.Name]" Action="Delete" Security="SecurityAccessLevel.Host" Class="btn btn-danger" OnClick="@(async () => await DeleteTheme(context))" ResourceKey="DeleteTheme" />
}
</td>
<td>@context.Name</td>
@ -34,7 +37,7 @@ else
<td>
@if (UpgradeAvailable(context.ThemeName, context.Version))
{
<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))>@Localizer["Upgrade"]</button>
}
</td>
<td></td>
@ -48,7 +51,7 @@ else
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
protected override async Task OnInitializedAsync()
protected override async Task OnParametersSetAsync()
{
try
{
@ -60,7 +63,7 @@ else
if (_themes == null)
{
await logger.LogError(ex, "Error Loading Themes {Error}", ex.Message);
AddModuleMessage("Error Loading Themes", MessageType.Error);
AddModuleMessage(Localizer["Error Loading Themes"], MessageType.Error);
}
}
}
@ -85,15 +88,13 @@ else
{
await PackageService.DownloadPackageAsync(themename, version, "Themes");
await logger.LogInformation("Theme Downloaded {ThemeName} {Version}", themename, version);
ShowProgressIndicator();
var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 3);
await ThemeService.InstallThemesAsync();
AddModuleMessage(Localizer["Theme Installed Successfully. You Must <a href=\"{0}\">Restart</a> Your Application To Apply These Changes.", NavigateUrl("admin/system")], MessageType.Success);
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Downloading Theme {ThemeName} {Version} {Error}", themename, version, ex.Message);
AddModuleMessage("Error Downloading Theme", MessageType.Error);
AddModuleMessage(Localizer["Error Downloading Theme"], MessageType.Error);
}
}
@ -101,15 +102,14 @@ else
{
try
{
ShowProgressIndicator();
var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 3);
await ThemeService.DeleteThemeAsync(Theme.ThemeName);
AddModuleMessage(Localizer["Theme Deleted Successfully"], MessageType.Success);
StateHasChanged();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Deleting Theme {Theme} {Error}", Theme, ex.Message);
AddModuleMessage("Error Deleting Theme", MessageType.Error);
AddModuleMessage(Localizer["Error Deleting Theme"], MessageType.Error);
}
}
}

View File

@ -1,13 +1,14 @@
@namespace Oqtane.Modules.Admin.Themes
@namespace Oqtane.Modules.Admin.Themes
@using System.Net
@inherits ModuleBase
@inject IThemeService ThemeService
@inject NavigationManager NavigationManager
@inject IStringLocalizer<View> Localizer
<table class="table table-borderless">
<tr>
<td>
<Label For="name" HelpText="The name of the theme">Name: </Label>
<Label For="name" HelpText="The name of the theme" ResourceKey="Name">Name: </Label>
</td>
<td>
<input id="name" class="form-control" @bind="@_name" disabled />
@ -15,7 +16,7 @@
</tr>
<tr>
<td>
<Label For="themename" HelpText="The internal name of the module">Internal Name: </Label>
<Label For="themename" HelpText="The internal name of the module" ResourceKey="InternalName">Internal Name: </Label>
</td>
<td>
<input id="themename" class="form-control" @bind="@_themeName" disabled />
@ -23,7 +24,7 @@
</tr>
<tr>
<td>
<Label For="version" HelpText="The version of the thene">Version: </Label>
<Label For="version" HelpText="The version of the theme" ResourceKey="Version">Version: </Label>
</td>
<td>
<input id="version" class="form-control" @bind="@_version" disabled />
@ -31,7 +32,7 @@
</tr>
<tr>
<td>
<Label For="owner" HelpText="The owner or creator of the theme">Owner: </Label>
<Label For="owner" HelpText="The owner or creator of the theme" ResourceKey="Owner">Owner: </Label>
</td>
<td>
<input id="owner" class="form-control" @bind="@_owner" disabled />
@ -39,7 +40,7 @@
</tr>
<tr>
<td>
<Label For="url" HelpText="The reference url of the theme">Reference Url: </Label>
<Label For="url" HelpText="The reference url of the theme" ResourceKey="ReferenceUrl">Reference Url: </Label>
</td>
<td>
<input id="url" class="form-control" @bind="@_url" disabled />
@ -47,7 +48,7 @@
</tr>
<tr>
<td>
<Label For="contact" HelpText="The contact for the theme">Contact: </Label>
<Label For="contact" HelpText="The contact for the theme" ResourceKey="Contact">Contact: </Label>
</td>
<td>
<input id="contact" class="form-control" @bind="@_contact" disabled />
@ -55,14 +56,14 @@
</tr>
<tr>
<td>
<Label For="license" HelpText="The license of the theme">License: </Label>
<Label For="license" HelpText="The license of the theme" ResourceKey="License">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>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@Localizer["Cancel"]</NavLink>
@code {
private string _themeName = "";
@ -95,7 +96,7 @@
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading Theme {ThemeName} {Error}", _themeName, ex.Message);
AddModuleMessage("Error Loading Theme", MessageType.Error);
AddModuleMessage(Localizer["Error Loading Theme"], MessageType.Error);
}
}
}

View File

@ -1,36 +1,37 @@
@namespace Oqtane.Modules.Admin.Upgrade
@namespace Oqtane.Modules.Admin.Upgrade
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IFileService FileService
@inject IPackageService PackageService
@inject IInstallationService InstallationService
@inject IStringLocalizer<Index> Localizer
@if (_package != null)
{
<TabStrip>
<TabPanel Name="Download">
<TabPanel Name="Download" ResourceKey="Download">
@if (_upgradeavailable)
{
<ModuleMessage Type="MessageType.Info" Message="Select The Upgrade Button To Install a New Framework Version"></ModuleMessage>
@("Framework") @_package.Version <button type="button" class="btn btn-success" @onclick=@(async () => await Download(Constants.PackageId, Constants.Version))>Upgrade</button>
<button type="button" class="btn btn-success" @onclick=@(async () => await Download(Constants.PackageId, @_package.Version))>@Localizer["Upgrade To"] @_package.Version</button>
}
else
{
<ModuleMessage Type="MessageType.Info" Message="Framework Is Already Up To Date"></ModuleMessage>
}
</TabPanel>
<TabPanel Name="Upload">
<TabPanel Name="Upload" ResourceKey="Upload">
<table class="table table-borderless">
<tr>
<td>
<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" ResourceKey="Framework">Framework: </Label>
</td>
<td>
<FileManager Filter="nupkg" Folder="Framework" />
<FileManager Filter="nupkg" ShowFiles="false" Folder="Framework" />
</td>
</tr>
</table>
<button type="button" class="btn btn-success" @onclick="Upgrade">Install</button>
<button type="button" class="btn btn-success" @onclick="Upgrade">@Localizer["Install"]</button>
</TabPanel>
</TabStrip>
}
@ -71,13 +72,13 @@
{
ShowProgressIndicator();
var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 3);
await interop.RedirectBrowser(NavigateUrl(), 10);
await InstallationService.Upgrade();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Executing Upgrade {Error}", ex.Message);
AddModuleMessage("Error Executing Upgrade", MessageType.Error);
AddModuleMessage(Localizer["Error Executing Upgrade"], MessageType.Error);
}
}
@ -88,13 +89,13 @@
await PackageService.DownloadPackageAsync(packageid, version, "Framework");
ShowProgressIndicator();
var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 3);
await interop.RedirectBrowser(NavigateUrl(), 10);
await InstallationService.Upgrade();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Downloading Framework {Error}", ex.Message);
AddModuleMessage("Error Downloading Framework", MessageType.Error);
AddModuleMessage(Localizer["Error Downloading Framework"], MessageType.Error);
}
}
}

View File

@ -1,15 +1,16 @@
@namespace Oqtane.Modules.Admin.UserProfile
@namespace Oqtane.Modules.Admin.UserProfile
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IUserService UserService
@inject INotificationService NotificationService
@inject INotificationService NotificationService
@inject IStringLocalizer<Add> Localizer
@if (PageState.User != null)
{
<table class="table table-borderless">
<tr>
<td>
<Label For="to" HelpText="Enter the username you wish to send a message to">To: </Label>
<Label For="to" HelpText="Enter the username you wish to send a message to" ResourceKey="To">To: </Label>
</td>
<td>
<input id="to" class="form-control" @bind="@username" />
@ -17,7 +18,7 @@
</tr>
<tr>
<td>
<Label For="subject" HelpText="Enter the subject of the message">Subject: </Label>
<Label For="subject" HelpText="Enter the subject of the message" ResourceKey="Subject">Subject: </Label>
</td>
<td>
<input id="subject" class="form-control" @bind="@subject" />
@ -25,15 +26,15 @@
</tr>
<tr>
<td>
<Label For="message" HelpText="Enter the message">Message: </Label>
<Label For="message" HelpText="Enter the message" ResourceKey="Message">Message: </Label>
</td>
<td>
<textarea id="message" class="form-control" @bind="@body" rows="5" />
</td>
</tr>
</table>
<button type="button" class="btn btn-primary" @onclick="Send">Send</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink>
<button type="button" class="btn btn-primary" @onclick="Send">@Localizer["Send"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@Localizer["Cancel"]</NavLink>
}
@code {
@ -47,39 +48,25 @@
private async Task Send()
{
var notification = new Notification();
try
{
var user = await UserService.GetUserAsync(username, PageState.Site.SiteId);
if (user != null)
{
notification.SiteId = PageState.Site.SiteId;
notification.FromUserId = PageState.User.UserId;
notification.FromDisplayName = PageState.User.DisplayName;
notification.FromEmail = PageState.User.Email;
notification.ToUserId = user.UserId;
notification.ToDisplayName = user.DisplayName;
notification.ToEmail = user.Email;
notification.Subject = subject;
notification.Body = body;
notification.ParentId = null;
notification.CreatedOn = DateTime.UtcNow;
notification.IsDelivered = false;
notification.DeliveredOn = null;
notification.SendOn = DateTime.UtcNow;
{
var notification = new Notification(PageState.Site.SiteId, PageState.User, user, subject, body, null);
notification = await NotificationService.AddNotificationAsync(notification);
await logger.LogInformation("Notification Created {Notification}", notification);
await logger.LogInformation("Notification Created {NotificationId}", notification.NotificationId);
NavigationManager.NavigateTo(NavigateUrl());
}
else
{
AddModuleMessage("User Does Not Exist. Please Verify That The Username Provided Is Correct.", MessageType.Warning);
AddModuleMessage(Localizer["User Does Not Exist. Please Verify That The Username Provided Is Correct."], MessageType.Warning);
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Adding Notification {Notification} {Error}", notification, ex.Message);
AddModuleMessage("Error Adding Notification", MessageType.Error);
await logger.LogError(ex, "Error Adding Notification {Error}", ex.Message);
AddModuleMessage(Localizer["Error Adding Notification"], MessageType.Error);
}
}

View File

@ -1,10 +1,11 @@
@namespace Oqtane.Modules.Admin.UserProfile
@namespace Oqtane.Modules.Admin.UserProfile
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IUserService UserService
@inject IProfileService ProfileService
@inject ISettingService SettingService
@inject INotificationService NotificationService
@inject IStringLocalizer<Index> Localizer
@if (PageState.User != null && photofileid != -1)
{
@ -15,13 +16,13 @@ else
<br />
}
<TabStrip>
<TabPanel Name="Identity">
<TabPanel Name="Identity" ResourceKey="Identity">
@if (PageState.User != null)
{
<table class="table table-borderless">
<tr>
<td>
<label for="Name" class="control-label">Username: </label>
<label for="Name" class="control-label">@Localizer["Username:"] </label>
</td>
<td>
<input class="form-control" @bind="@username" readonly />
@ -29,7 +30,7 @@ else
</tr>
<tr>
<td>
<label for="Name" class="control-label">Password: </label>
<label for="Name" class="control-label">@Localizer["Password:"] </label>
</td>
<td>
<input type="password" class="form-control" @bind="@password" autocomplete="new-password" />
@ -37,7 +38,7 @@ else
</tr>
<tr>
<td>
<label for="Name" class="control-label">Confirm Password: </label>
<label for="Name" class="control-label">@Localizer["Confirm Password:"] </label>
</td>
<td>
<input type="password" class="form-control" @bind="@confirm" autocomplete="new-password" />
@ -45,7 +46,7 @@ else
</tr>
<tr>
<td>
<label for="Name" class="control-label">Email: </label>
<label for="Name" class="control-label">@Localizer["Email:"] </label>
</td>
<td>
<input class="form-control" @bind="@email" />
@ -53,7 +54,7 @@ else
</tr>
<tr>
<td>
<label for="Name" class="control-label">Full Name: </label>
<label for="Name" class="control-label">@Localizer["Full Name:"] </label>
</td>
<td>
<input class="form-control" @bind="@displayname" />
@ -61,24 +62,26 @@ else
</tr>
<tr>
<td>
<label for="Name" class="control-label">Photo: </label>
<label for="Name" class="control-label">@Localizer["Photo:"] </label>
</td>
<td>
<FileManager FileId="@photofileid" @ref="filemanager" />
</td>
</tr>
</table>
<button type="button" class="btn btn-primary" @onclick="Save">Save</button>
<button type="button" class="btn btn-secondary" @onclick="Cancel">Cancel</button>
<button type="button" class="btn btn-primary" @onclick="Save">@Localizer["Save"]</button>
<button type="button" class="btn btn-secondary" @onclick="Cancel">@Localizer["Cancel"]</button>
}
</TabPanel>
<TabPanel Name="Profile">
<TabPanel Name="Profile" ResourceKey="Profile">
@if (profiles != null && settings != null)
{
<table class="table table-borderless">
@foreach (Profile profile in profiles)
<table class="table table-borderless">
@foreach (Profile profile in profiles)
{
var p = profile;
if (!p.IsPrivate || UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
{
var p = profile;
if (p.Category != category)
{
<tr>
@ -90,22 +93,49 @@ else
}
<tr>
<td>
<label for="@p.Name" class="control-label">@p.Title: </label>
<Label For="@p.Name" HelpText="@p.Description">@p.Title</Label>
</td>
<td>
<input class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" placeholder="@p.Description" @onchange="@(e => ProfileChanged(e, p.Name))" />
@if (!string.IsNullOrEmpty(p.Options))
{
<select id="@p.Name" class="form-control" @onchange="@(e => ProfileChanged(e, p.Name))">
@foreach (var option in p.Options.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
@if(GetProfileValue(p.Name, "") == option || (GetProfileValue(p.Name, "") == "" && p.DefaultValue == option))
{
<option value="@option" selected>@option</option>
}
else
{
<option value="@option">@option</option>
}
}
</select>
}
else
{
@if (p.IsRequired)
{
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" required @onchange="@(e => ProfileChanged(e, p.Name))" />
}
else
{
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" />
}
}
</td>
</tr>
}
</table>
<button type="button" class="btn btn-primary" @onclick="Save">Save</button>
<button type="button" class="btn btn-secondary" @onclick="Cancel">Cancel</button>
}
</table>
<button type="button" class="btn btn-primary" @onclick="Save">@Localizer["Save"]</button>
<button type="button" class="btn btn-secondary" @onclick="Cancel">@Localizer["Cancel"]</button>
}
</TabPanel>
<TabPanel Name="Notifications">
<TabPanel Name="Notifications" ResourceKey="Notifications">
@if (notifications != null)
{
<ActionLink Action="Add" Text="Send Notification" Security="SecurityAccessLevel.View" EditMode="false" />
<ActionLink Action="Add" Text="Send Notification" Security="SecurityAccessLevel.View" EditMode="false" ResourceKey="SendNotification" />
<br /><br />
@if (filter == "to")
{
@ -113,13 +143,13 @@ else
<Header>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>From</th>
<th>Subject</th>
<th>Received</th>
<th>@Localizer["From"]</th>
<th>@Localizer["Subject"]</th>
<th>@Localizer["Received"]</th>
</Header>
<Row>
<td><ActionLink Action="View" Parameters="@($"id=" + context.NotificationId.ToString())" Security="SecurityAccessLevel.View" EditMode="false" /></td>
<td><ActionDialog Header="Delete Notification" Message="@("Are You Sure You Wish To Delete This Notification?")" Action="Delete" Security="SecurityAccessLevel.View" Class="btn btn-danger" OnClick="@(async () => await Delete(context))" EditMode="false" /></td>
<td><ActionLink Action="View" Parameters="@($"id=" + context.NotificationId.ToString())" Security="SecurityAccessLevel.View" EditMode="false" ResourceKey="ViewNotification" /></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" ResourceKey="DeleteNotification" /></td>
<td>@context.FromDisplayName</td>
<td>@context.Subject</td>
<td>@context.CreatedOn</td>
@ -145,13 +175,13 @@ else
<Header>
<th>&nbsp;</th>
<th>&nbsp;</th>
<th>To</th>
<th>Subject</th>
<th>Sent</th>
<th>@Localizer["To"]</th>
<th>@Localizer["Subject"]</th>
<th>@Localizer["Sent"]</th>
</Header>
<Row>
<td><ActionLink Action="View" Parameters="@($"id=" + context.NotificationId.ToString())" Security="SecurityAccessLevel.View" EditMode="false" /></td>
<td><ActionDialog Header="Delete Notification" Message="@("Are You Sure You Wish To Delete This Notification?")" Action="Delete" Security="SecurityAccessLevel.View" Class="btn btn-danger" OnClick="@(async () => await Delete(context))" EditMode="false" /></td>
<td><ActionLink Action="View" Parameters="@($"id=" + context.NotificationId.ToString())" Security="SecurityAccessLevel.View" EditMode="false" ResourceKey="ViewNotification" /></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" ResourceKey="DeleteNotification" /></td>
<td>@context.ToDisplayName</td>
<td>@context.Subject</td>
<td>@context.CreatedOn</td>
@ -173,8 +203,8 @@ else
}
<br /><hr />
<select class="form-control" @onchange="(e => FilterChanged(e))">
<option value="to">Inbox</option>
<option value="from">Sent Items</option>
<option value="to">@Localizer["Inbox"]</option>
<option value="from">@Localizer["Sent Items"]</option>
</select>
}
</TabPanel>
@ -218,13 +248,13 @@ else
}
else
{
AddModuleMessage("Current User Is Not Logged In", MessageType.Warning);
AddModuleMessage(Localizer["Current User Is Not Logged In"], MessageType.Warning);
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading User Profile {Error}", ex.Message);
AddModuleMessage("Error Loading User Profile", MessageType.Error);
AddModuleMessage(Localizer["Error Loading User Profile"], MessageType.Error);
}
}
@ -241,7 +271,7 @@ else
{
try
{
if (username != string.Empty && email != string.Empty)
if (username != string.Empty && email != string.Empty && ValidateProfiles())
{
if (password == confirm)
{
@ -261,24 +291,45 @@ else
await UserService.UpdateUserAsync(user);
await SettingService.UpdateUserSettingsAsync(settings, PageState.User.UserId);
await logger.LogInformation("User Profile Saved");
AddModuleMessage(Localizer["User Profile Updated Successfully"], MessageType.Success);
}
else
{
AddModuleMessage("Passwords Entered Do Not Match", MessageType.Warning);
AddModuleMessage(Localizer["Passwords Entered Do Not Match"], MessageType.Warning);
}
}
else
{
AddModuleMessage("You Must Provide A Username and Email Address", MessageType.Warning);
AddModuleMessage(Localizer["You Must Provide A Username and Email Address As Well As All Required Profile Information"], MessageType.Warning);
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Saving User Profile {Error}", ex.Message);
AddModuleMessage("Error Saving User Profile", MessageType.Error);
AddModuleMessage(Localizer["Error Saving User Profile"], MessageType.Error);
}
}
private bool ValidateProfiles()
{
bool valid = true;
foreach (Profile profile in profiles)
{
if (string.IsNullOrEmpty(SettingService.GetSetting(settings, profile.Name, string.Empty)) && !string.IsNullOrEmpty(profile.DefaultValue))
{
settings = SettingService.SetSetting(settings, profile.Name, profile.DefaultValue);
}
if (!profile.IsPrivate || UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
{
if (profile.IsRequired && string.IsNullOrEmpty(SettingService.GetSetting(settings, profile.Name, string.Empty)))
{
valid = false;
}
}
}
return valid;
}
private void Cancel()
{
NavigationManager.NavigateTo(NavigateUrl(string.Empty));

View File

@ -1,15 +1,16 @@
@namespace Oqtane.Modules.Admin.UserProfile
@namespace Oqtane.Modules.Admin.UserProfile
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IUserService UserService
@inject INotificationService NotificationService
@inject INotificationService NotificationService
@inject IStringLocalizer<View> Localizer
@if (PageState.User != null)
{
<table class="table table-borderless">
<tr>
<td>
<label class="control-label">@title: </label>
<label class="control-label">@Localizer["Title:"] </label>
</td>
@if (title == "From")
{
@ -26,7 +27,7 @@
</tr>
<tr>
<td>
<label class="control-label">Subject: </label>
<label class="control-label">@Localizer["Subject:"] </label>
</td>
@if (title == "From")
{
@ -45,7 +46,7 @@
{
<tr>
<td>
<label class="control-label">Date: </label>
<label class="control-label">@Localizer["Date:"] </label>
</td>
<td>
<input class="form-control" @bind="@createdon" readonly />
@ -56,7 +57,7 @@
{
<tr>
<td>
<label class="control-label">Message: </label>
<label class="control-label">@Localizer["Message:"] </label>
</td>
<td>
<textarea class="form-control" @bind="@body" rows="5" readonly />
@ -67,7 +68,7 @@
{
<tr>
<td>
<label class="control-label">Message: </label>
<label class="control-label">@Localizer["Message:"] </label>
</td>
<td>
<textarea class="form-control" @bind="@body" rows="5" />
@ -79,24 +80,26 @@
@if (reply != string.Empty)
{
<button type="button" class="btn btn-primary" @onclick="Send">Send</button> }
<button type="button" class="btn btn-primary" @onclick="Send">@Localizer["Send"]</button>
}
else
{
if (title == "From")
{
<button type="button" class="btn btn-primary" @onclick="Reply">Reply</button>}
<button type="button" class="btn btn-primary" @onclick="Reply">@Localizer["Reply"]</button>
}
}
<NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@Localizer["Cancel"]</NavLink>
<br />
<br />
@if (title == "To")
{
<div class="control-group">
<label class="control-label">Original Message </label>
<label class="control-label">@Localizer["Original Message"] </label>
<textarea class="form-control" @bind="@reply" rows="5" readonly />
</div>
}
}
}
@code {
private int notificationid;
@ -155,7 +158,7 @@
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading Users {Error}", ex.Message);
AddModuleMessage("Error Loading Users", MessageType.Error);
AddModuleMessage(Localizer["Error Loading Users"], MessageType.Error);
}
}
@ -173,39 +176,25 @@
private async Task Send()
{
var notification = new Notification();
try
{
var user = await UserService.GetUserAsync(username, PageState.Site.SiteId);
if (user != null)
{
notification.SiteId = PageState.Site.SiteId;
notification.FromUserId = PageState.User.UserId;
notification.FromDisplayName = PageState.User.DisplayName;
notification.FromEmail = PageState.User.Email;
notification.ToUserId = user.UserId;
notification.ToDisplayName = user.DisplayName;
notification.ToEmail = user.Email;
notification.Subject = subject;
notification.Body = body;
notification.ParentId = notificationid;
notification.CreatedOn = DateTime.UtcNow;
notification.IsDelivered = false;
notification.DeliveredOn = null;
notification.SendOn = DateTime.UtcNow;
{
var notification = new Notification(PageState.Site.SiteId, PageState.User, user, subject, body, notificationid);
notification = await NotificationService.AddNotificationAsync(notification);
await logger.LogInformation("Notification Created {Notification}", notification);
await logger.LogInformation("Notification Created {NotificationId}", notification.NotificationId);
NavigationManager.NavigateTo(NavigateUrl());
}
else
{
AddModuleMessage("User Does Not Exist. Please Verify That The Username Provided Is Correct.", MessageType.Warning);
AddModuleMessage(Localizer["User Does Not Exist. Please Verify That The Username Provided Is Correct."], MessageType.Warning);
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Adding Notification {Notification} {Error}", notification, ex.Message);
AddModuleMessage("Error Adding Notification", MessageType.Error);
await logger.LogError(ex, "Error Adding Notification {Error}", ex.Message);
AddModuleMessage(Localizer["Error Adding Notification"], MessageType.Error);
}
}
}

View File

@ -1,18 +1,19 @@
@namespace Oqtane.Modules.Admin.Users
@namespace Oqtane.Modules.Admin.Users
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IUserService UserService
@inject IProfileService ProfileService
@inject ISettingService SettingService
@inject IStringLocalizer<Add> Localizer
<TabStrip>
<TabPanel Name="Identity">
<TabPanel Name="Identity" ResourceKey="Identity">
@if (profiles != null)
{
<table class="table table-borderless">
<tr>
<td>
<label class="control-label">Username: </label>
<label class="control-label">@Localizer["Username:"] </label>
</td>
<td>
<input class="form-control" @bind="@username" />
@ -20,7 +21,7 @@
</tr>
<tr>
<td>
<label class="control-label">Password: </label>
<label class="control-label">@Localizer["Password:"] </label>
</td>
<td>
<input type="password" class="form-control" @bind="@password" />
@ -28,7 +29,7 @@
</tr>
<tr>
<td>
<label class="control-label">Confirm Password: </label>
<label class="control-label">@Localizer["Confirm Password:"] </label>
</td>
<td>
<input type="password" class="form-control" @bind="@confirm" />
@ -36,7 +37,7 @@
</tr>
<tr>
<td>
<label class="control-label">Email: </label>
<label class="control-label">@Localizer["Email:"] </label>
</td>
<td>
<input class="form-control" @bind="@email" />
@ -44,7 +45,7 @@
</tr>
<tr>
<td>
<label class="control-label">Full Name: </label>
<label class="control-label">@Localizer["Full Name:"] </label>
</td>
<td>
<input class="form-control" @bind="@displayname" />
@ -53,38 +54,45 @@
</table>
}
</TabPanel>
<TabPanel Name="Profile">
<TabPanel Name="Profile" ResourceKey="Profile">
@if (profiles != null)
{
<table class="table table-borderless">
@foreach (Profile profile in profiles)
{
var p = profile;
if (p.Category != category)
<table class="table table-borderless">
@foreach (Profile profile in profiles)
{
var p = profile;
if (p.Category != category)
{
<tr>
<th colspan="2" style="text-align: center;">
@p.Category
</th>
</tr>
category = p.Category;
}
<tr>
<th colspan="2" style="text-align: center;">
@p.Category
</th>
<td>
<Label For="@p.Name" HelpText="@p.Description">@p.Title</Label>
</td>
<td>
@if (p.IsRequired)
{
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" required @onchange="@(e => ProfileChanged(e, p.Name))" />
}
else
{
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" />
}
</td>
</tr>
category = p.Category;
}
<tr>
<td>
<label for="@p.Name" class="control-label">@p.Title: </label>
</td>
<td>
<input class="form-control" maxlength="@p.MaxLength" placeholder="@p.Description" @onchange="@(e => ProfileChanged(e, p.Name))" />
</td>
</tr>
}
</table>
</table>
}
</TabPanel>
</TabStrip>
<button type="button" class="btn btn-primary" @onclick="SaveUser">Save</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink>
<button type="button" class="btn btn-primary" @onclick="SaveUser">@Localizer["Save"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@Localizer["Cancel"]</NavLink>
@code {
private string username = string.Empty;
@ -108,15 +116,18 @@
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading User Profile {Error}", ex.Message);
AddModuleMessage("Error Loading User Profile", MessageType.Error);
AddModuleMessage(Localizer["Error Loading User Profile"], MessageType.Error);
}
}
private string GetProfileValue(string SettingName, string DefaultValue)
=> SettingService.GetSetting(settings, SettingName, DefaultValue);
private async Task SaveUser()
{
try
{
if (username != string.Empty && password != string.Empty && confirm != string.Empty && email != string.Empty)
if (username != string.Empty && password != string.Empty && confirm != string.Empty && email != string.Empty && ValidateProfiles())
{
if (password == confirm)
{
@ -139,26 +150,43 @@
else
{
await logger.LogError("Error Adding User {Username} {Email}", username, email);
AddModuleMessage("Error Adding User. Please Ensure Password Meets Complexity Requirements And Username Is Not Already In Use.", MessageType.Error);
AddModuleMessage(Localizer["Error Adding User. Please Ensure Password Meets Complexity Requirements And Username Is Not Already In Use."], MessageType.Error);
}
}
else
{
AddModuleMessage("Passwords Entered Do Not Match", MessageType.Warning);
AddModuleMessage(Localizer["Passwords Entered Do Not Match"], MessageType.Warning);
}
}
else
{
AddModuleMessage("You Must Provide A Username, Password, and Email Address", MessageType.Warning);
AddModuleMessage(Localizer["You Must Provide A Username, Password, Email Address And All Required Profile Information"], MessageType.Warning);
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Adding User {Username} {Email} {Error}", username, email, ex.Message);
AddModuleMessage("Error Adding User", MessageType.Error);
AddModuleMessage(Localizer["Error Adding User"], MessageType.Error);
}
}
private bool ValidateProfiles()
{
bool valid = true;
foreach (Profile profile in profiles)
{
if (string.IsNullOrEmpty(SettingService.GetSetting(settings, profile.Name, string.Empty)) && !string.IsNullOrEmpty(profile.DefaultValue))
{
settings = SettingService.SetSetting(settings, profile.Name, profile.DefaultValue);
}
if (profile.IsRequired && string.IsNullOrEmpty(SettingService.GetSetting(settings, profile.Name, string.Empty)))
{
valid = false;
}
}
return valid;
}
private void ProfileChanged(ChangeEventArgs e, string SettingName)
{
var value = (string)e.Value;

View File

@ -1,9 +1,10 @@
@namespace Oqtane.Modules.Admin.Users
@namespace Oqtane.Modules.Admin.Users
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IUserService UserService
@inject IProfileService ProfileService
@inject ISettingService SettingService
@inject IStringLocalizer<Edit> Localizer
@if (PageState.User != null && photofileid != -1)
{
@ -14,13 +15,13 @@ else
<br />
}
<TabStrip>
<TabPanel Name="Identity">
<TabPanel Name="Identity" ResourceKey="Identity">
@if (profiles != null)
{
<table class="table table-borderless">
<tr>
<td>
<label class="control-label">Username: </label>
<label class="control-label">@Localizer["Username:"] </label>
</td>
<td>
<input class="form-control" @bind="@username" readonly />
@ -28,7 +29,7 @@ else
</tr>
<tr>
<td>
<label class="control-label">Password: </label>
<label class="control-label">@Localizer["Password:"] </label>
</td>
<td>
<input type="password" class="form-control" @bind="@password" />
@ -36,7 +37,7 @@ else
</tr>
<tr>
<td>
<label class="control-label">Confirm Password: </label>
<label class="control-label">@Localizer["Confirm Password:"] </label>
</td>
<td>
<input type="password" class="form-control" @bind="@confirm" />
@ -44,7 +45,7 @@ else
</tr>
<tr>
<td>
<label class="control-label">Email: </label>
<label class="control-label">@Localizer["Email:"] </label>
</td>
<td>
<input class="form-control" @bind="@email" />
@ -52,7 +53,7 @@ else
</tr>
<tr>
<td>
<label class="control-label">Full Name: </label>
<label class="control-label">@Localizer["Full Name:"] </label>
</td>
<td>
<input class="form-control" @bind="@displayname" />
@ -60,7 +61,7 @@ else
</tr>
<tr>
<td>
<label class="control-label">Photo: </label>
<label class="control-label">@Localizer["Photo:"] </label>
</td>
<td>
<FileManager FileId="@photofileid" @ref="filemanager" />
@ -68,51 +69,59 @@ else
</tr>
<tr>
<td>
<label class="control-label">Is Deleted? </label>
<label class="control-label">@Localizer["Is Deleted?"] </label>
</td>
<td>
<select class="form-control" @bind="@isdeleted">
<option value="True">Yes</option>
<option value="False">No</option>
<option value="True">@Localizer["Yes"]</option>
<option value="False">@Localizer["No"]</option>
</select>
</td>
</tr>
</table>
}
</TabPanel>
<TabPanel Name="Profile">
<TabPanel Name="Profile" ResourceKey="Profile">
@if (profiles != null)
{
<table class="table table-borderless">
@foreach (Profile profile in profiles)
{
var p = profile;
if (p.Category != category)
<table class="table table-borderless">
@foreach (Profile profile in profiles)
{
var p = profile;
if (p.Category != category)
{
<tr>
<th colspan="2" style="text-align: center;">
@p.Category
</th>
</tr>
category = p.Category;
}
<tr>
<th colspan="2" style="text-align: center;">
@p.Category
</th>
<td>
<Label For="@p.Name" HelpText="@p.Description">@p.Title</Label>
</td>
<td>
@if (p.IsRequired)
{
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" required @onchange="@(e => ProfileChanged(e, p.Name))" />
}
else
{
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" />
}
</td>
</tr>
category = p.Category;
}
<tr>
<td>
<label for="@p.Name" class="control-label">@p.Title: </label>
</td>
<td>
<input class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" placeholder="@p.Description" @onchange="@(e => ProfileChanged(e, p.Name))" />
</td>
</tr>
}
</table>
</table>
}
</TabPanel>
</TabStrip>
<button type="button" class="btn btn-primary" @onclick="SaveUser">Save</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink>
<br /><br />
<button type="button" class="btn btn-primary" @onclick="SaveUser">@Localizer["Save"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@Localizer["Cancel"]</NavLink>
<br />
<br />
<AuditInfo CreatedBy="@createdby" CreatedOn="@createdon" ModifiedBy="@modifiedby" ModifiedOn="@modifiedon" DeletedBy="@deletedby" DeletedOn="@deletedon"></AuditInfo>
@code {
@ -169,7 +178,7 @@ else
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading User {UserId} {Error}", userid, ex.Message);
AddModuleMessage("Error Loading User", MessageType.Error);
AddModuleMessage(Localizer["Error Loading User"], MessageType.Error);
}
}
@ -180,7 +189,7 @@ else
{
try
{
if (username != string.Empty && email != string.Empty)
if (username != string.Empty && email != string.Empty && ValidateProfiles())
{
if (password == confirm)
{
@ -208,21 +217,38 @@ else
}
else
{
AddModuleMessage("Passwords Entered Do Not Match", MessageType.Warning);
AddModuleMessage(Localizer["Passwords Entered Do Not Match"], MessageType.Warning);
}
}
else
{
AddModuleMessage("You Must Provide A Username, Password, and Email Address", MessageType.Warning);
AddModuleMessage(Localizer["You Must Provide A Username, Password, Email Address, And All Required Profile Information"], MessageType.Warning);
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Saving User {Username} {Email} {Error}", username, email, ex.Message);
AddModuleMessage("Error Saving User", MessageType.Error);
AddModuleMessage(Localizer["Error Saving User"], MessageType.Error);
}
}
private bool ValidateProfiles()
{
bool valid = true;
foreach (Profile profile in profiles)
{
if (string.IsNullOrEmpty(SettingService.GetSetting(settings, profile.Name, string.Empty)) && !string.IsNullOrEmpty(profile.DefaultValue))
{
settings = SettingService.SetSetting(settings, profile.Name, profile.DefaultValue);
}
if (profile.IsRequired && string.IsNullOrEmpty(SettingService.GetSetting(settings, profile.Name, string.Empty)))
{
valid = false;
}
}
return valid;
}
private void ProfileChanged(ChangeEventArgs e, string SettingName)
{
var value = (string)e.Value;

View File

@ -1,21 +1,22 @@
@namespace Oqtane.Modules.Admin.Users
@namespace Oqtane.Modules.Admin.Users
@inherits ModuleBase
@inject IUserRoleService UserRoleService
@inject IUserService UserService
@inject ISettingService SettingService
@inject IStringLocalizer<Index> Localizer
@if (userroles == null)
{
<p>
<em>Loading...</em>
<em>@Localizer["Loading..."]</em>
</p>
}
else
{
<ActionLink Action="Add" Text="Add User"/>
<ActionLink Action="Add" Text="Add User" ResourceKey="AddUser" />
<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>
<input class="form-control mr-4" @bind="@_search" /><button class="btn btn-outline-primary ml-1" @onclick="OnSearch">@Localizer["Search"]</button>
</div>
<Pager Items="@userroles">
@ -23,17 +24,17 @@ else
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>Name</th>
<th>@Localizer["Name"]</th>
</Header>
<Row>
<td>
<ActionLink Action="Edit" Parameters="@($"id=" + context.UserId.ToString())"/>
<ActionLink Action="Edit" Parameters="@($"id=" + context.UserId.ToString())" ResourceKey="EditUser" />
</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))"/>
<ActionDialog Header="Delete User" Message="@Localizer["Are You Sure You Wish To Delete {0}?", context.User.DisplayName]" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteUser(context))" ResourceKey="DeleteUser" />
</td>
<td>
<ActionLink Action="Roles" Parameters="@($"id=" + context.UserId.ToString())"/>
<ActionLink Action="Roles" Parameters="@($"id=" + context.UserId.ToString())" ResourceKey="Roles" />
</td>
<td>@context.User.DisplayName</td>
</Row>
@ -58,10 +59,10 @@ else
{
if (string.IsNullOrEmpty(_search))
{
return allroles.Where(item => item.Role.Name == Constants.RegisteredRole).ToList();
return allroles.Where(item => item.Role.Name == RoleNames.Registered).ToList();
}
return allroles
.Where(item => item.Role.Name == Constants.RegisteredRole &&
.Where(item => item.Role.Name == RoleNames.Registered &&
(
item.User.Username.Contains(search, StringComparison.OrdinalIgnoreCase) ||
item.User.Email.Contains(search, StringComparison.OrdinalIgnoreCase) ||
@ -84,7 +85,7 @@ else
var user = await UserService.GetUserAsync(UserRole.UserId, PageState.Site.SiteId);
if (user != null)
{
await UserService.DeleteUserAsync(user.UserId);
await UserService.DeleteUserAsync(user.UserId, PageState.Site.SiteId);
await logger.LogInformation("User Deleted {User}", UserRole.User);
StateHasChanged();
}

View File

@ -1,19 +1,20 @@
@namespace Oqtane.Modules.Admin.Users
@namespace Oqtane.Modules.Admin.Users
@inherits ModuleBase
@inject IRoleService RoleService
@inject IUserService UserService
@inject IUserRoleService UserRoleService
@inject IStringLocalizer<Roles> Localizer
@if (userroles == null)
{
<p><em>Loading...</em></p>
<p><em>@Localizer["Loading..."]</em></p>
}
else
{
<table class="table table-borderless">
<tr>
<td>
<Label For="user" HelpText="The user you are assigning roles to">User: </Label>
<Label For="user" HelpText="The user you are assigning roles to" ResourceKey="User">User: </Label>
</td>
<td>
<input id="user" class="form-control" @bind="@name" disabled />
@ -21,11 +22,11 @@ else
</tr>
<tr>
<td>
<Label For="role" HelpText="Select a role">Role: </Label>
<Label For="role" HelpText="Select a role" ResourceKey="Role">Role: </Label>
</td>
<td>
<select id="role" class="form-control" @bind="@roleid">
<option value="-1">&lt;Select Role&gt;</option>
<option value="-1">&lt;@Localizer["Select Role"]&gt;</option>
@foreach (Role role in roles)
{
<option value="@(role.RoleId)">@role.Name</option>
@ -35,7 +36,7 @@ else
</tr>
<tr>
<td>
<Label For="effectiveDate" HelpText="The date that this role assignment is active">Effective Date: </Label>
<Label For="effectiveDate" HelpText="The date that this role assignment is active" ResourceKey="EffectiveDate">Effective Date: </Label>
</td>
<td>
<input id="effectiveDate" class="form-control" @bind="@effectivedate" />
@ -43,30 +44,30 @@ else
</tr>
<tr>
<td>
<Label For="expiryDate" HelpText="The date that this role assignment expires">Expiry Date: </Label>
<Label For="expiryDate" HelpText="The date that this role assignment expires" ResourceKey="ExpiryDate">Expiry Date: </Label>
</td>
<td>
<input id="expiryDate" class="form-control" @bind="@expirydate" />
</td>
</tr>
</table>
<button type="button" class="btn btn-success" @onclick="SaveUserRole">Save</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink>
<button type="button" class="btn btn-success" @onclick="SaveUserRole">@Localizer["Save"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@Localizer["Cancel"]</NavLink>
<hr class="app-rule" />
<p align="center">
<Pager Items="@userroles">
<Header>
<th>Roles</th>
<th>@Localizer["Roles"]</th>
<th>&nbsp;</th>
</Header>
<Row>
<td>@context.Role.Name</td>
<td>
@if (context.Role.Name != Constants.RegisteredRole)
{
<button type="button" class="btn btn-danger" @onclick=@(async () => await DeleteUserRole(context.UserRoleId))>Delete</button>
}
@if (context.Role.Name != RoleNames.Registered)
{
<button type="button" class="btn btn-danger" @onclick=@(async () => await DeleteUserRole(context.UserRoleId))>@Localizer["Delete"]</button>
}
</td>
</Row>
</Pager>
@ -97,7 +98,7 @@ else
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading Roles {Error}", ex.Message);
AddModuleMessage("Error Loading Roles", MessageType.Error);
AddModuleMessage(Localizer["Error Loading Roles"], MessageType.Error);
}
}
@ -111,7 +112,7 @@ else
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading User Roles {UserId} {Error}", userid, ex.Message);
AddModuleMessage("Error Loading User Roles", MessageType.Error);
AddModuleMessage(Localizer["Error Loading User Roles"], MessageType.Error);
}
}
@ -172,17 +173,17 @@ else
await GetUserRoles();
await logger.LogInformation("User Assigned To Role {UserRole}", userrole);
AddModuleMessage("User Assigned To Role", MessageType.Success);
AddModuleMessage(Localizer["User Assigned To Role"], MessageType.Success);
}
else
{
AddModuleMessage("You Must Select A Role", MessageType.Warning);
AddModuleMessage(Localizer["You Must Select A Role"], MessageType.Warning);
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Saving User Roles {UserId} {Error}", userid, ex.Message);
AddModuleMessage("Error Saving User Roles", MessageType.Error);
AddModuleMessage(Localizer["Error Saving User Roles"], MessageType.Error);
}
}
@ -193,12 +194,12 @@ else
await UserRoleService.DeleteUserRoleAsync(UserRoleId);
await GetUserRoles();
await logger.LogInformation("User Removed From Role {UserRoleId}", UserRoleId);
AddModuleMessage("User Removed From Role", MessageType.Success);
AddModuleMessage(Localizer["User Removed From Role"], MessageType.Success);
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Removing User From Role {UserRoleId} {Error}", UserRoleId, ex.Message);
AddModuleMessage("Error Removing User From Role", MessageType.Error);
AddModuleMessage(Localizer["Error Removing User From Role"], MessageType.Error);
}
}
}

View File

@ -1,9 +1,9 @@
@namespace Oqtane.Modules.Controls
@inherits ModuleBase
@attribute [OqtaneIgnore]
@namespace Oqtane.Modules.Controls
@inherits LocalizableComponent
@if (_visible)
{
<div class="app-admin-modal">
<div class="app-actiondialog">
<div class="modal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
@ -17,9 +17,9 @@
<div class="modal-footer">
@if (!string.IsNullOrEmpty(Action))
{
<button type="button" class="@Class" @onclick="Confirm">@((MarkupString)_iconSpan) @Action</button>
<button type="button" class="@Class" @onclick="Confirm">@((MarkupString)_iconSpan) @Localize(Action)</button>
}
<button type="button" class="btn btn-secondary" @onclick="DisplayModal">Cancel</button>
<button type="button" class="btn btn-secondary" @onclick="DisplayModal">@Localize("Cancel")</button>
</div>
</div>
</div>
@ -40,7 +40,7 @@
@code {
private bool _visible = false;
private bool _editmode = true;
private bool _editmode = false;
private bool _authorized = false;
private string _iconSpan = string.Empty;
@ -66,7 +66,7 @@
public bool Disabled { get; set; } // optional
[Parameter]
public string EditMode { get; set; } // optional - specifies if a user must be in edit mode to see the action - default is true
public string EditMode { get; set; } // optional - specifies if an authorized user must be in edit mode to see the action - default is false
[Parameter]
public Action OnClick { get; set; } // required if an Action is specified - executes a method in the calling component
@ -76,6 +76,8 @@
protected override void OnParametersSet()
{
base.OnParametersSet();
if (string.IsNullOrEmpty(Text))
{
Text = Action;
@ -84,6 +86,7 @@
{
Class = "btn btn-success";
}
if (!string.IsNullOrEmpty(EditMode))
{
_editmode = bool.Parse(EditMode);
@ -91,9 +94,17 @@
if (!string.IsNullOrEmpty(IconName))
{
_iconSpan = $"<span class=\"oi oi-{IconName}\"></span>&nbsp;";
if (!IconName.Contains(" "))
{
IconName = "oi oi-" + IconName;
}
_iconSpan = $"<span class=\"{IconName}\"></span>&nbsp;";
}
Text = Localize(nameof(Text), Text);
Header = Localize(nameof(Header), Header);
Message = Localize(nameof(Message), Message);
_authorized = IsAuthorized();
}
@ -133,10 +144,10 @@
authorized = UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, ModuleState.Permissions);
break;
case SecurityAccessLevel.Admin:
authorized = UserSecurity.IsAuthorized(PageState.User, Constants.AdminRole);
authorized = UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin);
break;
case SecurityAccessLevel.Host:
authorized = UserSecurity.IsAuthorized(PageState.User, Constants.HostRole);
authorized = UserSecurity.IsAuthorized(PageState.User, RoleNames.Host);
break;
}
}

View File

@ -1,6 +1,5 @@
@namespace Oqtane.Modules.Controls
@inherits ModuleBase
@attribute [OqtaneIgnore]
@namespace Oqtane.Modules.Controls
@inherits LocalizableComponent
@inject IUserService UserService
@if (_authorized)
@ -21,7 +20,7 @@
private string _parameters = string.Empty;
private string _classname = "btn btn-primary";
private string _style = string.Empty;
private bool _editmode = true;
private bool _editmode = false;
private bool _authorized = false;
private string _iconSpan = string.Empty;
@ -47,16 +46,18 @@
public bool Disabled { get; set; } // optional
[Parameter]
public string EditMode { get; set; } // optional - specifies if a user must be in edit mode to see the action - default is true
public string EditMode { get; set; } // optional - specifies if an authorized user must be in edit mode to see the action - default is false.
[Parameter]
public string IconName { get; set; } // optional - specifies an icon for the link - default is no icon
[Parameter]
public bool IconOnly { get; set; } // optional - specifies only icon in link
protected override void OnParametersSet()
{
base.OnParametersSet();
_text = Action;
if (!string.IsNullOrEmpty(Text))
{
@ -90,10 +91,15 @@
if (!string.IsNullOrEmpty(IconName))
{
_iconSpan = $"<span class=\"oi oi-{IconName}\"></span>{(IconOnly?"":"&nbsp")}";
if (!IconName.Contains(" "))
{
IconName = "oi oi-" + IconName;
}
_iconSpan = $"<span class=\"{IconName}\"></span>{(IconOnly ? "" : "&nbsp")}";
}
_text = Localize(nameof(Text), _text);
_url = EditUrl(Action, _parameters);
_authorized = IsAuthorized();
}
@ -123,7 +129,7 @@
{
security = Security.Value;
}
switch (security)
{
case SecurityAccessLevel.Anonymous:
@ -136,14 +142,14 @@
authorized = UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, ModuleState.Permissions);
break;
case SecurityAccessLevel.Admin:
authorized = UserSecurity.IsAuthorized(PageState.User, Constants.AdminRole);
authorized = UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin);
break;
case SecurityAccessLevel.Host:
authorized = UserSecurity.IsAuthorized(PageState.User, Constants.HostRole);
authorized = UserSecurity.IsAuthorized(PageState.User, RoleNames.Host);
break;
}
}
return authorized;
}
}

View File

@ -1,6 +1,6 @@
@namespace Oqtane.Modules.Controls
@inherits ModuleBase
@attribute [OqtaneIgnore]
@namespace Oqtane.Modules.Controls
@inherits ModuleControlBase
@inject IStringLocalizer<AuditInfo> Localizer
@if (_text != string.Empty)
{
@ -8,21 +8,21 @@
}
@code {
private string _text = string.Empty;
[Parameter]
public string CreatedBy { get; set; }
[Parameter]
public DateTime CreatedOn { get; set; }
public DateTime? CreatedOn { get; set; }
[Parameter]
public string ModifiedBy { get; set; }
[Parameter]
public DateTime ModifiedOn { get; set; }
public DateTime? ModifiedOn { get; set; }
[Parameter]
public string DeletedBy { get; set; }
@ -38,54 +38,54 @@
protected override void OnParametersSet()
{
_text = string.Empty;
if (!String.IsNullOrEmpty(CreatedBy) || CreatedOn != null)
if (!String.IsNullOrEmpty(CreatedBy) || CreatedOn.HasValue)
{
_text += "<p style=\"" + Style + "\">Created ";
_text += $"<p style=\"{Style}\">{Localizer["Created"]} ";
if (!String.IsNullOrEmpty(CreatedBy))
{
_text += " by <b>" + CreatedBy + "</b>";
_text += $" {Localizer["by"]} <b>{CreatedBy}</b>";
}
if (CreatedOn != null)
{
_text += " on <b>" + CreatedOn.ToString("MMM dd yyyy HH:mm:ss") + "</b>";
_text += $" {Localizer["on"]} <b>{CreatedOn.Value.ToString("MMM dd yyyy HH:mm:ss")}</b>";
}
_text += "</p>";
}
if (!String.IsNullOrEmpty(ModifiedBy) || ModifiedOn != null)
if (!String.IsNullOrEmpty(ModifiedBy) || ModifiedOn.HasValue)
{
_text += "<p style=\"" + Style + "\">Last modified ";
_text += $"<p style=\"{Style}\">{Localizer["Last modified"]} ";
if (!String.IsNullOrEmpty(ModifiedBy))
{
_text += " by <b>" + ModifiedBy + "</b>";
_text += $" {Localizer["by"]} <b>{ModifiedBy}</b>";
}
if (ModifiedOn != null)
{
_text += " on <b>" + ModifiedOn.ToString("MMM dd yyyy HH:mm:ss") + "</b>";
_text += $" {Localizer["on"]} <b>{ModifiedOn.Value.ToString("MMM dd yyyy HH:mm:ss")}</b>";
}
_text += "</p>";
}
if (!String.IsNullOrEmpty(DeletedBy) || DeletedOn.HasValue)
{
_text += "<p style=\"" + Style + "\">Deleted ";
_text += $"<p style=\"{Style}\">{Localizer["Deleted"]} ";
if (!String.IsNullOrEmpty(DeletedBy))
{
_text += " by <b>" + DeletedBy + "</b>";
_text += $" {Localizer["by"]} <b>{DeletedBy}</b>";
}
if (DeletedOn != null)
{
_text += " on <b>" + DeletedOn.Value.ToString("MMM dd yyyy HH:mm:ss") + "</b>";
_text += $" {Localizer["on"]} <b>{DeletedOn.Value.ToString("MMM dd yyyy HH:mm:ss")}</b>";
}
_text += "</p>";
}
}

View File

@ -1,9 +1,8 @@
@namespace Oqtane.Modules.Controls
@inherits ModuleBase
@attribute [OqtaneIgnore]
@namespace Oqtane.Modules.Controls
@inherits ModuleControlBase
@inject IFolderService FolderService
@inject IFileService FileService
@inject IStringLocalizer<FileManager> Localizer
@if (_folders != null)
{
@ -13,21 +12,14 @@
@if (ShowFolders || FolderId <= 0)
{
<div>
<select class="form-control" @onchange="(e => FolderChanged(e))">
<select class="form-control" value="@FolderId" @onchange="(e => FolderChanged(e))">
@if (string.IsNullOrEmpty(Folder))
{
<option value="-1">&lt;Select Folder&gt;</option>
<option value="-1">&lt;@Localizer["Select Folder"]&gt;</option>
}
@foreach (Folder folder in _folders)
{
if (folder.FolderId == FolderId)
{
<option value="@(folder.FolderId)" selected>@(new string('-', folder.Level * 2))@(folder.Name)</option>
}
else
{
<option value="@(folder.FolderId)">@(new string('-', folder.Level * 2))@(folder.Name)</option>
}
<option value="@(folder.FolderId)">@(new string('-', folder.Level * 2))@(folder.Name)</option>
}
</select>
</div>
@ -35,18 +27,11 @@
@if (ShowFiles)
{
<div>
<select class="form-control" @onchange="(e => FileChanged(e))">
<option value="-1">&lt;Select File&gt;</option>
<select class="form-control" value="@FileId" @onchange="(e => FileChanged(e))">
<option value="-1">&lt;@Localizer["Select File"]&gt;</option>
@foreach (File file in _files)
{
if (file.FileId == FileId)
{
<option value="@(file.FileId)" selected>@(file.Name)</option>
}
else
{
<option value="@(file.FileId)">@(file.Name)</option>
}
<option value="@(file.FileId)">@(file.Name)</option>
}
</select>
</div>
@ -56,23 +41,23 @@
<div>
@if (UploadMultiple)
{
<input type="file" id="@_fileinputid" name="file" accept="@_filter" multiple/>
<input type="file" id="@_fileinputid" name="file" accept="@_filter" multiple />
}
else
{
<input type="file" id="@_fileinputid" name="file" accept="@_filter"/>
<input type="file" id="@_fileinputid" name="file" accept="@_filter" />
}
<span id="@_progressinfoid"></span><progress id="@_progressbarid" style="width: 150px; visibility: hidden;"></progress>
<span class="float-right">
<button type="button" class="btn btn-success" @onclick="UploadFile">Upload</button>
@if (_showfiles && GetFileId() != -1)
<button type="button" class="btn btn-success" @onclick="UploadFile">@Localizer["Upload"]</button>
@if (ShowFiles && GetFileId() != -1)
{
<button type="button" class="btn btn-danger" @onclick="DeleteFile">Delete</button>
<button type="button" class="btn btn-danger" @onclick="DeleteFile">@Localizer["Delete"]</button>
}
</span>
</div>
}
@((MarkupString) _message)
<ModuleMessage Message="@_message" Type="@_messagetype"></ModuleMessage>
</div>
@if (_image != string.Empty)
{
@ -88,15 +73,15 @@
private string _id;
private List<Folder> _folders;
private List<File> _files = new List<File>();
private bool _showfiles = true;
private string _fileinputid = string.Empty;
private string _progressinfoid = string.Empty;
private string _progressbarid = string.Empty;
private string _filter = "*";
private bool _haseditpermission = false;
private string _message = string.Empty;
private string _image = string.Empty;
private string _guid;
private string _message = string.Empty;
private MessageType _messagetype;
[Parameter]
public string Id { get; set; } // optional - for setting the id of the FileManager component for accessibility
@ -134,7 +119,7 @@
if (!string.IsNullOrEmpty(Folder))
{
_folders = new List<Folder> {new Folder {FolderId = -1, Name = Folder}};
_folders = new List<Folder> { new Folder { FolderId = -1, Name = Folder } };
FolderId = -1;
}
else
@ -163,7 +148,7 @@
await GetFiles();
// create unique id for component
// create unique id for component
_guid = Guid.NewGuid().ToString("N");
_fileinputid = _guid + "FileInput";
_progressinfoid = _guid + "ProgressInfo";
@ -175,7 +160,7 @@
_haseditpermission = false;
if (!string.IsNullOrEmpty(Folder))
{
_haseditpermission = UserSecurity.IsAuthorized(PageState.User, Constants.HostRole);
_haseditpermission = UserSecurity.IsAuthorized(PageState.User, RoleNames.Host);
_files = await FileService.GetFilesAsync(Folder);
}
else
@ -211,7 +196,7 @@
_message = string.Empty;
try
{
FolderId = int.Parse((string) e.Value);
FolderId = int.Parse((string)e.Value);
await GetFiles();
FileId = -1;
_image = string.Empty;
@ -220,14 +205,16 @@
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading Files {Error}", ex.Message);
_message = "<br /><div class=\"alert alert-danger\" role=\"alert\">Error Loading Files</div>";
_message = Localizer["Error Loading Files"];
_messagetype = MessageType.Error;
}
}
private async Task FileChanged(ChangeEventArgs e)
{
_message = string.Empty;
FileId = int.Parse((string) e.Value);
FileId = int.Parse((string)e.Value);
await SetImage();
StateHasChanged();
@ -244,8 +231,8 @@
var maxwidth = 200;
var maxheight = 200;
var ratioX = (double) maxwidth / (double) file.ImageWidth;
var ratioY = (double) maxheight / (double) file.ImageHeight;
var ratioX = (double)maxwidth / (double)file.ImageWidth;
var ratioY = (double)maxheight / (double)file.ImageHeight;
var ratio = ratioX < ratioY ? ratioX : ratioY;
_image = "<img src=\"" + ContentUrl(FileId) + "\" alt=\"" + file.Name +
@ -257,6 +244,7 @@
private async Task UploadFile()
{
_message = string.Empty;
var interop = new Interop(JSRuntime);
var upload = await interop.GetFiles(_fileinputid);
if (upload.Length > 0)
@ -276,7 +264,10 @@
if (result == string.Empty)
{
await logger.LogInformation("File Upload Succeeded {Files}", upload);
_message = "<br /><div class=\"alert alert-success\" role=\"alert\">File Upload Succeeded</div>";
_message = Localizer["File Upload Succeeded"];
_messagetype = MessageType.Success;
await GetFiles();
if (upload.Length == 1)
@ -293,30 +284,37 @@
else
{
await logger.LogError("File Upload Failed For {Files}", result.Replace(",", ", "));
_message = "<br /><div class=\"alert alert-danger\" role=\"alert\">File Upload Failed</div>";
_message = Localizer["File Upload Failed"];
_messagetype = MessageType.Error;
}
}
catch (Exception ex)
{
await logger.LogError(ex, "File Upload Failed {Error}", ex.Message);
_message = "<br /><div class=\"alert alert-danger\" role=\"alert\">File Upload Failed</div>";
_message = Localizer["File Upload Failed"];
_messagetype = MessageType.Error;
}
}
else
{
_message = "<br /><div class=\"alert alert-warning\" role=\"alert\">You Have Not Selected A File To Upload</div>";
_message = Localizer["You Have Not Selected A File To Upload"];
_messagetype = MessageType.Warning;
}
}
private async Task DeleteFile()
{
_message = string.Empty;
try
{
await FileService.DeleteFileAsync(FileId);
await logger.LogInformation("File Deleted {File}", FileId);
_message = "<br /><div class=\"alert alert-success\" role=\"alert\">File Deleted</div>";
_message = Localizer["File Deleted"];
_messagetype = MessageType.Success;
await GetFiles();
FileId = -1;
await SetImage();
@ -325,7 +323,9 @@
catch (Exception ex)
{
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 = Localizer["Error Deleting File"];
_messagetype = MessageType.Error;
}
}

View File

@ -1,6 +1,5 @@
@namespace Oqtane.Modules.Controls
@inherits ModuleBase
@attribute [OqtaneIgnore]
@namespace Oqtane.Modules.Controls
@inherits LocalizableComponent
@if (!string.IsNullOrEmpty(HelpText))
{
@ -16,7 +15,7 @@ else
private string _closeLabel = "</label>";
[Parameter]
public RenderFragment ChildContent { get; set; }
public RenderFragment ChildContent { get; set; }
[Parameter]
public string For { get; set; } // optional - the id of the associated input control for accessibility
@ -29,17 +28,27 @@ else
protected override void OnParametersSet()
{
base.OnParametersSet();
_openLabel = "<label";
if (!string.IsNullOrEmpty(For))
{
_openLabel += " for=\"" + For + "\"";
}
if (!string.IsNullOrEmpty(Class))
{
_openLabel += " class=\"" + Class + "\"";
}
_openLabel += ">";
var text = Localize("Text", String.Empty);
if (text != String.Empty)
{
ChildContent =@<text>@text</text>;
}
HelpText = Localize(nameof(HelpText), HelpText);
}
}

View File

@ -0,0 +1,69 @@
using System;
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Localization;
using Oqtane.Shared;
namespace Oqtane.Modules.Controls
{
public class LocalizableComponent : ModuleControlBase
{
private IStringLocalizer _localizer;
[Parameter]
public string ResourceKey { get; set; }
protected bool IsLocalizable { get; private set; }
protected string Localize(string name) => _localizer?[name] ?? name;
protected string Localize(string propertyName, string propertyValue)
{
if (!IsLocalizable)
{
return propertyValue;
}
var key = $"{ResourceKey}.{propertyName}";
var value = Localize(key);
if (value == key)
{
// Returns default property value (English version) instead of ResourceKey.PropertyName
return propertyValue;
}
else
{
if (value == String.Empty)
{
// Returns default property value (English version)
return propertyValue;
}
else
{
return value;
}
}
}
protected override void OnParametersSet()
{
IsLocalizable = false;
if (!String.IsNullOrEmpty(ResourceKey) && ModuleState?.ModuleType != null)
{
var moduleType = Type.GetType(ModuleState.ModuleType);
if (moduleType != null)
{
using (var scope = ServiceActivator.GetScope())
{
var localizerFactory = scope.ServiceProvider.GetService<IStringLocalizerFactory>();
_localizer = localizerFactory.Create(moduleType);
IsLocalizable = true;
}
}
}
}
}
}

View File

@ -1,16 +1,22 @@
@namespace Oqtane.Modules.Controls
@inherits ModuleBase
@attribute [OqtaneIgnore]
@namespace Oqtane.Modules.Controls
@inherits ModuleControlBase
@inject NavigationManager NavigationManager
@if (!string.IsNullOrEmpty(_message))
{
<div class="@_classname" role="alert">@_message</div>
<div class="@_classname" role="alert">
@((MarkupString)_message)
@if (Type == MessageType.Error && PageState != null && UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
{
@((MarkupString)"&nbsp;&nbsp;")<NavLink href="@NavigateUrl("admin/log")">View Details</NavLink>
}
</div>
<br />
}
@code {
private string _message = string.Empty;
private string _classname = "alert alert-danger";
private string _classname = string.Empty;
[Parameter]
public string Message { get; set; }
@ -20,23 +26,16 @@
protected override void OnParametersSet()
{
if (!string.IsNullOrEmpty(Message))
_message = Message;
if (!string.IsNullOrEmpty(_message))
{
_message = Message;
_classname = GetMessageType(Type);
}
}
public void SetModuleMessage(string message, MessageType type)
{
_message = message;
_classname = GetMessageType(type);
StateHasChanged();
}
private string GetMessageType(MessageType type)
{
var classname = string.Empty;
string classname = string.Empty;
switch (type)
{
case MessageType.Success:
@ -52,7 +51,7 @@
classname = "alert alert-danger";
break;
}
return classname;
}
}

View File

@ -1,81 +1,130 @@
@namespace Oqtane.Modules.Controls
@inherits ModuleBase
@attribute [OqtaneIgnore]
@typeparam TableItem
@namespace Oqtane.Modules.Controls
@inherits ModuleControlBase
@typeparam TableItem
<p>
@if(Format == "Table")
@if (Toolbar == "Top")
{
<table class="@Class">
<thead>
<tr>@Header</tr>
</thead>
<tbody>
<div class="mx-auto text-center">
@if (_endPage > 1)
{
<button class="btn btn-secondary mr-1" @onclick=@(async () => UpdateList(1))><span class="oi oi-media-step-backward" title="first" aria-hidden="true"></span></button>
}
@if (_page > _maxPages)
{
<button class="btn btn-secondary mr-1" @onclick=@(async () => SetPagerSize("back"))><span class="oi oi-media-skip-backward" title="back" aria-hidden="true"></span></button>
}
@if (_endPage > 1)
{
<button class="btn btn-secondary mr-1" @onclick=@(async () => NavigateToPage("previous"))><span class="oi oi-chevron-left" title="previous" aria-hidden="true"></span></button>
@for (int i = _startPage; i <= _endPage; i++)
{
var pager = i;
<button class="btn @((pager == _page) ? "btn-primary" : "btn-link")" @onclick=@(async () => UpdateList(pager))>
@pager
</button>
}
<button class="btn btn-secondary mr-1" @onclick=@(async () => NavigateToPage("next"))><span class="oi oi-chevron-right" title="next" aria-hidden="true"></span></button>
}
@if (_endPage < _pages)
{
<button class="btn btn-secondary mr-1" @onclick=@(async () => SetPagerSize("forward"))><span class="oi oi-media-skip-forward" title="forward" aria-hidden="true"></span></button>
}
@if (_endPage > 1)
{
<button class="btn btn-secondary mr-1" @onclick=@(async () => UpdateList(_pages))><span class="oi oi-media-step-forward" title="last" aria-hidden="true"></span></button>
}
@if (_endPage > 1)
{
<span class="btn btn-link disabled">Page @_page of @_pages</span>
}
</div>
}
@if (Format == "Table")
{
<table class="@Class">
<thead>
<tr>@Header</tr>
</thead>
<tbody>
@foreach (var item in ItemList)
{
<tr>@Row(item)</tr>
@if (Detail != null)
{
<tr>@Detail(item)</tr>
}
}
</tbody>
</table>
}
@if (Format == "Grid")
{
<div class="@Class">
<div class="row">@Header</div>
@foreach (var item in ItemList)
{
<tr>@Row(item)</tr>
<div class="row">@Row(item)</div>
@if (Detail != null)
{
<tr>@Detail(item)</tr>
<div class="row">@Detail(item)</div>
}
}
</tbody>
</table>
</div>
}
@if(Format == "Grid")
@if (Toolbar == "Bottom")
{
<div class="@Class">
<div class="row">@Header</div>
@foreach (var item in ItemList)
{
<div class="row">@Row(item)</div>
@if (Detail != null)
<div class="mx-auto text-center">
@if (_endPage > 1)
{
<div class="row">@Detail(item)</div>
<button class="btn btn-secondary mr-1" @onclick=@(async () => UpdateList(1))><span class="oi oi-media-step-backward" title="first" aria-hidden="true"></span></button>
}
}
</div>
@if (_page > _maxPages)
{
<button class="btn btn-secondary mr-1" @onclick=@(async () => SetPagerSize("back"))><span class="oi oi-media-skip-backward" title="back" aria-hidden="true"></span></button>
}
@if (_endPage > 1)
{
<button class="btn btn-secondary mr-1" @onclick=@(async () => NavigateToPage("previous"))><span class="oi oi-chevron-left" title="previous" aria-hidden="true"></span></button>
@for (int i = _startPage; i <= _endPage; i++)
{
var pager = i;
<button class="btn @((pager == _page) ? "btn-primary" : "btn-link")" @onclick=@(async () => UpdateList(pager))>
@pager
</button>
}
<button class="btn btn-secondary mr-1" @onclick=@(async () => NavigateToPage("next"))><span class="oi oi-chevron-right" title="next" aria-hidden="true"></span></button>
}
@if (_endPage < _pages)
{
<button class="btn btn-secondary mr-1" @onclick=@(async () => SetPagerSize("forward"))><span class="oi oi-media-skip-forward" title="forward" aria-hidden="true"></span></button>
}
@if (_endPage > 1)
{
<button class="btn btn-secondary mr-1" @onclick=@(async () => UpdateList(_pages))><span class="oi oi-media-step-forward" title="last" aria-hidden="true"></span></button>
}
@if (_endPage > 1)
{
<span class="btn btn-link disabled">Page @_page of @_pages</span>
}
</div>
}
<div class="mx-auto text-center">
@if (_page > _maxPages)
{
<button class="btn btn-secondary" @onclick=@(async () => SetPagerSize("back"))><span class="oi oi-media-skip-backward" title="back" aria-hidden="true"></span></button>
}
@if (_endPage > 1)
{
<button class="btn btn-secondary" @onclick=@(async () => NavigateToPage("previous"))><span class="oi oi-chevron-left" title="previous" aria-hidden="true"></span></button>
@for (int i = _startPage; i <= _endPage; i++)
{
var pager = i;
<button class="btn @((pager == _page) ? "btn-primary" : "btn-link")" @onclick=@(async () => UpdateList(pager))>
@pager
</button>
}
<button class="btn btn-secondary" @onclick=@(async () => NavigateToPage("next"))><span class="oi oi-chevron-right" title="next" aria-hidden="true"></span></button>
}
@if (_endPage < _pages)
{
<button class="btn btn-secondary" @onclick=@(async () => SetPagerSize("forward"))><span class="oi oi-media-skip-forward" title="forward" aria-hidden="true"></span></button>
}
@if (_endPage > 1)
{
<span class="btn btn-link disabled">Page @_page of @_pages</span>
}
</div>
</p>
@code {
private int _pages = 0;
private int _page = 1;
private int _maxItems;
private int _maxPages;
private int _startPage;
private int _endPage;
private int _maxItems = 10;
private int _maxPages = 5;
private int _startPage = 0;
private int _endPage = 0;
[Parameter]
public string Format { get; set; }
[Parameter]
public string Toolbar { get; set; }
[Parameter]
public RenderFragment Header { get; set; }
@ -105,7 +154,12 @@
{
Format = "Table";
}
if (string.IsNullOrEmpty(Toolbar))
{
Toolbar = "Top";
}
if (string.IsNullOrEmpty(Class))
{
if (Format == "Table")
@ -117,25 +171,21 @@
Class = "container";
}
}
if (string.IsNullOrEmpty(PageSize))
{
_maxItems = 10;
}
else
if (!string.IsNullOrEmpty(PageSize))
{
_maxItems = int.Parse(PageSize);
}
if (string.IsNullOrEmpty(DisplayPages))
{
_maxPages = 5;
}
else
if (!string.IsNullOrEmpty(DisplayPages))
{
_maxPages = int.Parse(DisplayPages);
}
_page = 1;
_startPage = 0;
_endPage = 0;
if (Items != null)
{
ItemList = Items.Skip((_page - 1) * _maxItems).Take(_maxItems);
@ -149,7 +199,7 @@
{
ItemList = Items.Skip((currentPage - 1) * _maxItems).Take(_maxItems);
_page = currentPage;
StateHasChanged();
}
@ -174,7 +224,7 @@
{
_endPage = _pages;
}
StateHasChanged();
}
else if (direction == "back")
@ -208,7 +258,7 @@
_page -= 1;
}
}
UpdateList(_page);
}
}

View File

@ -1,8 +1,8 @@
@namespace Oqtane.Modules.Controls
@inherits ModuleBase
@attribute [OqtaneIgnore]
@namespace Oqtane.Modules.Controls
@inherits ModuleControlBase
@inject IRoleService RoleService
@inject IUserService UserService
@inject IStringLocalizer<PermissionGrid> Localizer
@if (_permissions != null)
{
@ -10,10 +10,10 @@
<table class="table" style="width: 50%; min-width: 250px;">
<tbody>
<tr>
<th scope="col">Role</th>
<th scope="col">@Localizer["Role"]</th>
@foreach (PermissionString permission in _permissions)
{
<th style="text-align: center; width: 1px;">@permission.PermissionName</th>
<th style="text-align: center; width: 1px;">@Localizer[permission.PermissionName]</th>
}
</tr>
@foreach (Role role in _roles)
@ -36,10 +36,10 @@
<table class="table" style="width: 50%; min-width: 250px;">
<thead>
<tr>
<th scope="col">User</th>
<th scope="col">@Localizer["User"]</th>
@foreach (PermissionString permission in _permissions)
{
<th style="text-align: center; width: 1px;">@permission.PermissionName</th>
<th style="text-align: center; width: 1px;">@Localizer[permission.PermissionName]</th>
}
</tr>
</thead>
@ -48,7 +48,7 @@
{
string userid = "[" + user.UserId.ToString() + "]";
<tr>
<td >@user.DisplayName</td>
<td>@user.DisplayName</td>
@foreach (PermissionString permission in _permissions)
{
var p = permission;
@ -65,8 +65,8 @@
<tbody>
<tr>
<td class="input-group">
<input type="text" name="Username" class="form-control" placeholder="Enter Username" @bind="@_username" />
<button type="button" class="btn btn-primary" @onclick="AddUser">Add</button>
<input type="text" name="Username" class="form-control" placeholder="@Localizer["Enter Username"]" @bind="@_username" />
<button type="button" class="btn btn-primary" @onclick="AddUser">@Localizer["Add"]</button>
</td>
</tr>
</tbody>
@ -104,16 +104,16 @@
}
_roles = await RoleService.GetRolesAsync(ModuleState.SiteId);
_roles.Insert(0, new Role { Name = Constants.AllUsersRole });
_roles.Insert(0, new Role { Name = RoleNames.Everyone });
_permissions = new List<PermissionString>();
foreach (string permissionname in _permissionnames.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
// initialize with admin role
_permissions.Add(new PermissionString { PermissionName = permissionname, Permissions = Constants.AdminRole });
_permissions.Add(new PermissionString { PermissionName = permissionname, Permissions = RoleNames.Admin });
}
if (!string.IsNullOrEmpty(Permissions))
{
// populate permissions
@ -123,7 +123,7 @@
{
_permissions[_permissions.FindIndex(item => item.PermissionName == permissionstring.PermissionName)].Permissions = permissionstring.Permissions;
}
if (permissionstring.Permissions.Contains("["))
{
foreach (string user in permissionstring.Permissions.Split(new char[] { '[' }, StringSplitOptions.RemoveEmptyEntries))
@ -162,7 +162,7 @@
}
private bool GetPermissionDisabled(string roleName)
=> roleName == Constants.AdminRole
=> roleName == RoleNames.Admin
? true
: false;
@ -180,10 +180,10 @@
}
catch
{
_message = "Username Does Not Exist";
_message = Localizer["Username Does Not Exist"];
}
}
_username = string.Empty;
}
@ -209,7 +209,7 @@
case null:
break; // permission not specified
}
_permissions[_permissions.FindIndex(item => item.PermissionName == permissionName)].Permissions = string.Join(";", ids.ToArray());
}
}
@ -227,8 +227,8 @@
{
permission = _permissions[i];
List<string> ids = permission.Permissions.Split(';').ToList();
ids.Remove("!" + Constants.AllUsersRole); // remove deny all users
ids.Remove("!" + Constants.RegisteredRole); // remove deny registered users
ids.Remove("!" + RoleNames.Everyone); // remove deny all users
ids.Remove("!" + RoleNames.Registered); // remove deny registered users
permission.Permissions = string.Join(";", ids.ToArray());
_permissions[i] = permission;
}

View File

@ -1,26 +1,29 @@
@namespace Oqtane.Modules.Controls
@inherits ModuleBase
@attribute [OqtaneIgnore]
@namespace Oqtane.Modules.Controls
@inherits ModuleControlBase
@inject IStringLocalizer<RichTextEditor> Localizer
<div class="row" style="margin-bottom: 50px;">
<div class="col">
<TabStrip>
<TabPanel Name="Rich" Heading="Rich Text Editor">
@if (_filemanagervisible)
@if (AllowFileManagement)
{
<FileManager @ref="_fileManager" Filter="@Constants.ImageFiles" />
@((MarkupString)_message)
<br />
}
<div class="row justify-content-center" style="margin-bottom: 20px;">
<button type="button" class="btn btn-secondary" @onclick="RefreshRichText">Synchronize Content</button>&nbsp;&nbsp;
<button type="button" class="btn btn-primary" @onclick="InsertImage">Insert Image</button>
@if (_filemanagervisible)
{
@((MarkupString)"&nbsp;&nbsp;")
<button type="button" class="btn btn-secondary" @onclick="CloseFileManager">Close</button>
<FileManager @ref="_fileManager" Filter="@Constants.ImageFiles" />
<ModuleMessage Message="@_message" Type="MessageType.Warning"></ModuleMessage>
<br />
}
</div>
<div class="row justify-content-center" style="margin-bottom: 20px;">
<button type="button" class="btn btn-secondary" @onclick="RefreshRichText">@Localizer["Synchronize Content"]</button>&nbsp;&nbsp;
<button type="button" class="btn btn-primary" @onclick="InsertImage">@Localizer["Insert Image"]</button>
@if (_filemanagervisible)
{
@((MarkupString)"&nbsp;&nbsp;")
<button type="button" class="btn btn-secondary" @onclick="CloseFileManager">@Localizer["Close"]</button>
}
</div>
}
<div class="row">
<div class="col">
<div @ref="@_toolBar">
@ -62,9 +65,9 @@
</div>
</div>
</TabPanel>
<TabPanel Name="Raw" Heading="Raw HTML Editor">
<TabPanel Name="Raw" Heading="Raw HTML Editor" ResourceKey="HtmlEditor">
<div class="row justify-content-center" style="margin-bottom: 20px;">
<button type="button" class="btn btn-secondary" @onclick="RefreshRawHtml">Synchronize Content</button>
<button type="button" class="btn btn-secondary" @onclick="RefreshRawHtml">@Localizer["Synchronize Content"]</button>
</div>
@if (ReadOnly)
{
@ -107,13 +110,16 @@
[Parameter]
public string DebugLevel { get; set; } = "info";
[Parameter]
public bool AllowFileManagement { get; set; } = true;
public override List<Resource> Resources => new List<Resource>()
{
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill1.3.6.min.js" },
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill-blot-formatter.min.js" },
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill-interop.js" }
};
protected override void OnInitialized()
{
_content = Content; // raw HTML
@ -182,6 +188,7 @@
public async Task InsertImage()
{
_message = string.Empty;
if (_filemanagervisible)
{
var fileid = _fileManager.GetFileId();
@ -190,17 +197,15 @@
var interop = new RichTextEditorInterop(JSRuntime);
await interop.InsertImage(_editorElement, ContentUrl(fileid));
_filemanagervisible = false;
_message = string.Empty;
}
else
{
_message = "<br /><div class=\"alert alert-warning\" role=\"alert\">You Must Select An Image To Insert</div>";
_message = Localizer["You Must Select An Image To Insert"];
}
}
else
{
_filemanagervisible = true;
_message = string.Empty;
}
StateHasChanged();
}

View File

@ -1,6 +1,5 @@
@namespace Oqtane.Modules.Controls
@inherits ModuleBase
@attribute [OqtaneIgnore]
@namespace Oqtane.Modules.Controls
@inherits LocalizableComponent
<div class="d-flex">
<div>
@ -17,13 +16,14 @@
<div class="d-flex">
<hr class="app-rule" />
</div>
<div class="collapse" id="@Name">
<div class="collapse @_show" id="@Name">
@ChildContent
</div>
@code {
private string _heading = string.Empty;
private string _expanded = string.Empty;
private string _show = string.Empty;
[Parameter]
public RenderFragment ChildContent { get; set; }
@ -41,5 +41,15 @@
{
_heading = (!string.IsNullOrEmpty(Heading)) ? Heading : Name;
_expanded = (!string.IsNullOrEmpty(Expanded)) ? Expanded : "false";
if (_expanded == "true") { _show = "show"; }
}
protected override void OnParametersSet()
{
base.OnParametersSet();
_heading = !string.IsNullOrEmpty(Heading)
? Localize(nameof(Heading), Heading)
: Localize(nameof(Name), Name);
}
}

View File

@ -1,6 +1,5 @@
@namespace Oqtane.Modules.Controls
@inherits ModuleBase
@attribute [OqtaneIgnore]
@namespace Oqtane.Modules.Controls
@inherits LocalizableComponent
@if (Name == Parent.ActiveTab)
{
@ -28,9 +27,27 @@ else
[Parameter]
public string Heading { get; set; } // optional - defaults to name if not specified
protected override void OnInitialized()
[Parameter]
public SecurityAccessLevel? Security { get; set; } // optional - can be used to specify SecurityAccessLevel
protected override void OnParametersSet()
{
base.OnInitialized();
base.OnParametersSet();
Parent.AddTabPanel((TabPanel)this);
if (string.IsNullOrEmpty(Heading))
{
Name = Localize(nameof(Name), Name);
}
else
{
Heading = Localize(nameof(Heading), Heading);
}
}
public string DisplayHeading()
{
return (string.IsNullOrEmpty(Heading)) ? Name : Heading;
}
}

View File

@ -1,27 +1,29 @@
@namespace Oqtane.Modules.Controls
@inherits ModuleBase
@attribute [OqtaneIgnore]
@namespace Oqtane.Modules.Controls
@inherits ModuleControlBase
<CascadingValue Value="this">
<CascadingValue Value="this" IsFixed="true">
<div class="container-fluid">
<div class="form-group">
<ul class="nav nav-tabs" role="tablist">
@foreach (TabPanel tabPanel in _tabPanels)
{
<li class="nav-item">
@if (tabPanel.Name == ActiveTab)
{
<a class="nav-link active" data-toggle="tab" href="#@tabPanel.Name" role="tab" @onclick:preventDefault="true">
@DisplayHeading(tabPanel.Name, tabPanel.Heading)
</a>
}
else
{
<a class="nav-link" data-toggle="tab" href="#@tabPanel.Name" role="tab" @onclick:preventDefault="true">
@DisplayHeading(tabPanel.Name, tabPanel.Heading)
</a>
}
</li>
@if (IsAuthorized(tabPanel))
{
<li class="nav-item" @key="tabPanel.Name">
@if (tabPanel.Name == ActiveTab)
{
<a class="nav-link active" data-toggle="tab" href="#@tabPanel.Name" role="tab" @onclick:preventDefault="true">
@tabPanel.DisplayHeading()
</a>
}
else
{
<a class="nav-link" data-toggle="tab" href="#@tabPanel.Name" role="tab" @onclick:preventDefault="true">
@tabPanel.DisplayHeading()
</a>
}
</li>
}
}
</ul>
<div class="tab-content">
@ -33,7 +35,7 @@
</CascadingValue>
@code {
private List<TabPanel> _tabPanels = new List<TabPanel>();
private List<TabPanel> _tabPanels;
[Parameter]
public RenderFragment ChildContent { get; set; } // contains the TabPanels
@ -49,18 +51,48 @@
}
}
internal void AddTabPanel(TabPanel tabPanel)
protected override void OnParametersSet()
{
_tabPanels.Add(tabPanel);
if (string.IsNullOrEmpty(ActiveTab))
{
ActiveTab = tabPanel.Name;
}
StateHasChanged();
_tabPanels = new List<TabPanel>();
}
private string DisplayHeading(string Name, string Heading)
internal void AddTabPanel(TabPanel tabPanel)
{
return (string.IsNullOrEmpty(Heading)) ? Name : Heading;
if (!_tabPanels.Exists(item => item.Name == tabPanel.Name))
{
_tabPanels.Add(tabPanel);
if (string.IsNullOrEmpty(ActiveTab))
{
ActiveTab = tabPanel.Name;
}
StateHasChanged();
}
}
private bool IsAuthorized(TabPanel tabPanel)
{
var authorized = false;
switch (tabPanel.Security)
{
case null: // security not specified - assume SecurityAccessLevel.Anonymous
authorized = true;
break;
case SecurityAccessLevel.Anonymous:
authorized = true;
break;
case SecurityAccessLevel.View:
authorized = UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, ModuleState.Permissions);
break;
case SecurityAccessLevel.Edit:
authorized = UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, ModuleState.Permissions);
break;
case SecurityAccessLevel.Admin:
authorized = UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin);
break;
case SecurityAccessLevel.Host:
authorized = UserSecurity.IsAuthorized(PageState.User, RoleNames.Host);
break;
}
return authorized;
}
}

View File

@ -1,4 +1,6 @@
@namespace Oqtane.Modules.Controls
@namespace Oqtane.Modules.Controls
@inherits ModuleControlBase
@inject IStringLocalizer<TriStateCheckBox> Localizer
<img src="@_src" title="@_title" @onclick="SetValue" />
@ -38,7 +40,7 @@
_value = true;
break;
}
SetImage();
OnChange(_value);
}
@ -50,11 +52,11 @@
{
case true:
_src = "images/checked.png";
_title = "Permission Granted";
_title = Localizer["Permission Granted"];
break;
case false:
_src = "images/unchecked.png";
_title = "Permission Denied";
_title = Localizer["Permission Denied"];
break;
case null:
_src = "images/null.png";

View File

@ -1,16 +1,18 @@
@using Oqtane.Modules.HtmlText.Services
@using Oqtane.Modules.HtmlText.Services
@using Oqtane.Modules.HtmlText.Models
@using Oqtane.Modules.Controls
@namespace Oqtane.Modules.HtmlText
@inherits ModuleBase
@inject IHtmlTextService HtmlTextService
@inject ISettingService SettingService
@inject NavigationManager NavigationManager
@inject IStringLocalizer<Edit> Localizer
@if (_content != null)
{
<RichTextEditor Content="@_content" @ref="@RichTextEditorHtml"></RichTextEditor>
<button type="button" class="btn btn-success" @onclick="SaveContent">Save</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink>
<RichTextEditor Content="@_content" AllowFileManagement="@_allowfilemanagement" @ref="@RichTextEditorHtml"></RichTextEditor>
<button type="button" class="btn btn-success" @onclick="SaveContent">@Localizer["Save"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@Localizer["Cancel"]</NavLink>
@if (!string.IsNullOrEmpty(_content))
{
<br />
@ -25,13 +27,14 @@
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 bool _allowfilemanagement;
private string _content = null;
private string _createdby;
private DateTime _createdon;
@ -42,6 +45,8 @@
{
try
{
_allowfilemanagement = bool.Parse(SettingService.GetSetting(ModuleState.Settings, "AllowFileManagement", "true"));
var htmltext = await HtmlTextService.GetHtmlTextAsync(ModuleState.ModuleId);
if (htmltext != null)
{
@ -91,7 +96,7 @@
catch (Exception ex)
{
await logger.LogError(ex, "Error Saving Content {Error}", ex.Message);
AddModuleMessage("Error Saving Content", MessageType.Error);
AddModuleMessage(Localizer["Error Saving Content"], MessageType.Error);
}
}
}

View File

@ -1,4 +1,4 @@
@using Oqtane.Modules.HtmlText.Services
@using Oqtane.Modules.HtmlText.Services
@namespace Oqtane.Modules.HtmlText
@inherits ModuleBase
@inject IHtmlTextService HtmlTextService
@ -7,7 +7,7 @@
@if (PageState.EditMode)
{
<br /><ActionLink Action="Edit" /><br /><br />
<br /><ActionLink Action="Edit" EditMode="true" ResourceKey="Edit" /><br /><br />
}
@code {

View File

@ -1,4 +1,4 @@
using Oqtane.Models;
using Oqtane.Models;
namespace Oqtane.Modules.HtmlText
{
@ -10,7 +10,8 @@ namespace Oqtane.Modules.HtmlText
Description = "Renders HTML or Text Content",
Version = "1.0.0",
ServerManagerType = "Oqtane.Modules.HtmlText.Manager.HtmlTextManager, Oqtane.Server",
ReleaseVersions = "1.0.0"
ReleaseVersions = "1.0.0",
SettingsType = "Oqtane.Modules.HtmlText.Settings, Oqtane.Client"
};
}
}

View File

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

View File

@ -0,0 +1,49 @@
@namespace Oqtane.Modules.HtmlText
@inherits ModuleBase
@inject ISettingService SettingService
@implements Oqtane.Interfaces.ISettingsControl
@inject IStringLocalizer<Settings> Localizer
<table class="table table-borderless">
<tr>
<td>
<Label For="files" ResourceKey="Allow File Management" HelpText="Specify If Editors Can Upload and Select Files">Allow File Management: </Label>
</td>
<td>
<select id="files" class="form-control" @bind="@_allowfilemanagement">
<option value="true">@Localizer["Yes"]</option>
<option value="false">@Localizer["No"]</option>
</select>
</td>
</tr>
</table>
@code {
private string _allowfilemanagement;
protected override void OnInitialized()
{
try
{
_allowfilemanagement = SettingService.GetSetting(ModuleState.Settings, "AllowFileManagement", "true");
}
catch (Exception ex)
{
ModuleInstance.AddModuleMessage(ex.Message, MessageType.Error);
}
}
public async Task UpdateSettings()
{
try
{
var settings = ModuleState.Settings;
settings = SettingService.SetSetting(settings, "AllowFileManagement", _allowfilemanagement);
await SettingService.UpdateModuleSettingsAsync(settings, ModuleState.ModuleId);
}
catch (Exception ex)
{
ModuleInstance.AddModuleMessage(ex.Message, MessageType.Error);
}
}
}

View File

@ -5,6 +5,7 @@
Success,
Info,
Warning,
Error
Error,
Undefined
}
}

View File

@ -1,4 +1,4 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components;
using Oqtane.Shared;
using Oqtane.Models;
using System.Threading.Tasks;
@ -17,7 +17,7 @@ namespace Oqtane.Modules
private Logger _logger;
protected Logger logger => _logger ?? (_logger = new Logger(this));
[Inject]
protected ILogService LoggingService { get; set; }
@ -30,7 +30,7 @@ namespace Oqtane.Modules
[CascadingParameter]
protected Module ModuleState { get; set; }
[CascadingParameter]
[CascadingParameter]
protected ModuleInstance ModuleInstance { get; set; }
// optional interface properties
@ -53,16 +53,19 @@ namespace Oqtane.Modules
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))
foreach (Resource resource in Resources.Where(item => item.ResourceType == ResourceType.Script && item.Declaration != ResourceDeclaration.Global))
{
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());
if (scripts.Any())
{
var interop = new Interop(JSRuntime);
await interop.IncludeScripts(scripts.ToArray());
}
}
}
}
// path method
public string ModulePath()
@ -113,7 +116,60 @@ namespace Oqtane.Modules
public string ContentUrl(int fileid)
{
return Utilities.ContentUrl(PageState.Alias, fileid);
return ContentUrl(fileid, false);
}
public string ContentUrl(int fileid, bool asAttachment)
{
return Utilities.ContentUrl(PageState.Alias, fileid, asAttachment);
}
public virtual Dictionary<string, string> GetUrlParameters(string parametersTemplate = "")
{
var urlParameters = new Dictionary<string, string>();
string[] templateSegments;
var parameters = PageState.UrlParameters.Split('/', StringSplitOptions.RemoveEmptyEntries);
var parameterId = 0;
if (string.IsNullOrEmpty(parametersTemplate))
{
for (int i = 0; i < parameters.Length; i++)
{
urlParameters.TryAdd("parameter" + i, parameters[i]);
}
}
else
{
templateSegments = parametersTemplate.Split('/', StringSplitOptions.RemoveEmptyEntries);
if (parameters.Length == templateSegments.Length)
{
for (int i = 0; i < parameters.Length; i++)
{
if (parameters.Length > i)
{
if (templateSegments[i] == parameters[i])
{
urlParameters.TryAdd("parameter" + parameterId, parameters[i]);
parameterId++;
}
else if (templateSegments[i].StartsWith("{") && templateSegments[i].EndsWith("}"))
{
var key = templateSegments[i].Replace("{", "");
key = key.Replace("}", "");
urlParameters.TryAdd(key, parameters[i]);
}
else
{
i = parameters.Length;
urlParameters.Clear();
}
}
}
}
}
return urlParameters;
}
// user feedback methods
@ -122,6 +178,11 @@ namespace Oqtane.Modules
ModuleInstance.AddModuleMessage(message, type);
}
public void ClearModuleMessage()
{
ModuleInstance.AddModuleMessage("", MessageType.Undefined);
}
public void ShowProgressIndicator()
{
ModuleInstance.ShowProgressIndicator();
@ -154,12 +215,15 @@ namespace Oqtane.Modules
case "add":
logFunction = LogFunction.Create;
break;
case "edit":
logFunction = LogFunction.Update;
break;
case "delete":
logFunction = LogFunction.Delete;
break;
default:
logFunction = LogFunction.Read;
break;

View File

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

View File

@ -1,12 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<OutputType>Exe</OutputType>
<LangVersion>7.3</LangVersion>
<RazorLangVersion>3.0</RazorLangVersion>
<Configurations>Debug;Release</Configurations>
<Version>1.0.1</Version>
<Version>2.0.2</Version>
<Product>Oqtane</Product>
<Authors>Shaun Walker</Authors>
<Company>.NET Foundation</Company>
@ -15,24 +14,19 @@
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
<RepositoryUrl>https://github.com/oqtane</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v1.0.1</PackageReleaseNotes>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.2</PackageReleaseNotes>
<RootNamespace>Oqtane</RootNamespace>
<IsPackable>true</IsPackable>
<BlazorWebAssemblyLoadAllGlobalizationData>true</BlazorWebAssemblyLoadAllGlobalizationData>
</PropertyGroup>
<ItemGroup>
<Compile Remove="Modules\Admin\ModuleCreator\Templates\**" />
<Content Remove="Modules\Admin\ModuleCreator\Templates\**" />
<EmbeddedResource Remove="Modules\Admin\ModuleCreator\Templates\**" />
<None Remove="Modules\Admin\ModuleCreator\Templates\**" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="3.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Build" Version="3.2.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="3.2.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="3.1.4" />
<PackageReference Include="System.Net.Http.Json" Version="3.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="5.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="5.0.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="5.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Localization" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Localization" Version="5.0.0" />
<PackageReference Include="System.Net.Http.Json" Version="5.0.0" />
</ItemGroup>
<ItemGroup>

View File

@ -1,19 +1,23 @@
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Microsoft.Extensions.DependencyInjection;
using System.Threading.Tasks;
using Oqtane.Services;
using System.Reflection;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Json;
using Oqtane.Modules;
using Oqtane.Shared;
using Oqtane.Providers;
using System.Reflection;
using System.Runtime.Loader;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Authorization;
using System.IO.Compression;
using System.IO;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Microsoft.AspNetCore.Localization;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.JSInterop;
using Oqtane.Modules;
using Oqtane.Providers;
using Oqtane.Services;
using Oqtane.Shared;
using Oqtane.UI;
namespace Oqtane.Client
{
@ -28,6 +32,9 @@ namespace Oqtane.Client
builder.Services.AddSingleton(httpClient);
builder.Services.AddOptions();
// Register localization services
builder.Services.AddLocalization(options => options.ResourcesPath = "Resources");
// register auth services
builder.Services.AddAuthorizationCore();
builder.Services.AddScoped<IdentityAuthenticationStateProvider>();
@ -59,35 +66,51 @@ namespace Oqtane.Client
builder.Services.AddScoped<ISiteTemplateService, SiteTemplateService>();
builder.Services.AddScoped<ISqlService, SqlService>();
builder.Services.AddScoped<ISystemService, SystemService>();
builder.Services.AddScoped<ILocalizationService, LocalizationService>();
builder.Services.AddScoped<ILanguageService, LanguageService>();
await LoadClientAssemblies(httpClient);
// dynamically register module contexts and repository services
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (Assembly assembly in assemblies)
var assemblies = AppDomain.CurrentDomain.GetOqtaneAssemblies();
foreach (var assembly in assemblies)
{
var implementationTypes = assembly.GetTypes()
.Where(item => item.GetInterfaces().Contains(typeof(IService)));
foreach (Type implementationtype in implementationTypes)
// dynamically register module services
var implementationTypes = assembly.GetInterfaces<IService>();
foreach (var implementationType in implementationTypes)
{
Type servicetype = Type.GetType(implementationtype.AssemblyQualifiedName.Replace(implementationtype.Name, "I" + implementationtype.Name));
if (servicetype != null)
if (implementationType.AssemblyQualifiedName != null)
{
builder.Services.AddScoped(servicetype, implementationtype); // traditional service interface
}
else
{
builder.Services.AddScoped(implementationtype, implementationtype); // no interface defined for service
var serviceType = Type.GetType(implementationType.AssemblyQualifiedName.Replace(implementationType.Name, $"I{implementationType.Name}"));
builder.Services.AddScoped(serviceType ?? implementationType, implementationType);
}
}
assembly.GetInstances<IClientStartup>()
.ToList()
.ForEach(x => x.ConfigureServices(builder.Services));
// register client startup services
var startUps = assembly.GetInstances<IClientStartup>();
foreach (var startup in startUps)
{
startup.ConfigureServices(builder.Services);
}
}
await builder.Build().RunAsync();
var host = builder.Build();
var jsRuntime = host.Services.GetRequiredService<IJSRuntime>();
var interop = new Interop(jsRuntime);
var localizationCookie = await interop.GetCookie(CookieRequestCultureProvider.DefaultCookieName);
var culture = CookieRequestCultureProvider.ParseCookieValue(localizationCookie).UICultures[0].Value;
var localizationService = host.Services.GetRequiredService<ILocalizationService>();
var cultures = await localizationService.GetCulturesAsync();
if (culture == null || !cultures.Any(c => c.Name.Equals(culture, StringComparison.OrdinalIgnoreCase)))
{
culture = cultures.Single(c => c.IsDefault).Name;
}
SetCulture(culture);
ServiceActivator.Configure(host.Services);
await host.RunAsync();
}
private static async Task LoadClientAssemblies(HttpClient http)
@ -101,24 +124,24 @@ namespace Oqtane.Client
// 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[]>();
var dlls = new Dictionary<string, byte[]>();
var pdbs = new Dictionary<string, byte[]>();
foreach (ZipArchiveEntry entry in archive.Entries)
{
if (!assemblies.Contains(Path.GetFileNameWithoutExtension(entry.Name)))
if (!assemblies.Contains(Path.GetFileNameWithoutExtension(entry.FullName)))
{
using (var memoryStream = new MemoryStream())
{
entry.Open().CopyTo(memoryStream);
byte[] file = memoryStream.ToArray();
switch (Path.GetExtension(entry.Name))
switch (Path.GetExtension(entry.FullName))
{
case ".dll":
dlls.Add(entry.Name, file);
dlls.Add(entry.FullName, file);
break;
case ".pdb":
pdbs.Add(entry.Name, file);
pdbs.Add(entry.FullName, file);
break;
}
}
@ -129,14 +152,21 @@ namespace Oqtane.Client
{
if (pdbs.ContainsKey(item.Key))
{
Assembly.Load(item.Value, pdbs[item.Key]);
AssemblyLoadContext.Default.LoadFromStream(new MemoryStream(item.Value), new MemoryStream(pdbs[item.Key]));
}
else
{
Assembly.Load(item.Value);
AssemblyLoadContext.Default.LoadFromStream(new MemoryStream(item.Value));
}
}
}
}
private static void SetCulture(string culture)
{
var cultureInfo = CultureInfo.GetCultureInfo(culture);
CultureInfo.DefaultThreadCurrentCulture = cultureInfo;
CultureInfo.DefaultThreadCurrentUICulture = cultureInfo;
}
}
}

View File

View File

@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Threading;
@ -37,11 +37,11 @@ namespace Oqtane.Services
{
if (!(folderPath.EndsWith(System.IO.Path.DirectorySeparatorChar) || folderPath.EndsWith(System.IO.Path.AltDirectorySeparatorChar)))
{
folderPath = Utilities.PathCombine(folderPath,"\\");
folderPath = Utilities.PathCombine(folderPath, System.IO.Path.DirectorySeparatorChar.ToString());
}
var path = WebUtility.UrlEncode(folderPath);
return await GetJsonAsync<List<File>>($"{Apiurl}/{siteId}/{path}");
}

View File

@ -1,4 +1,4 @@
using Oqtane.Models;
using Oqtane.Models;
using System.Threading.Tasks;
using System.Linq;
using System.Net.Http;
@ -35,13 +35,7 @@ namespace Oqtane.Services
public async Task<Folder> GetFolderAsync(int siteId, [NotNull] string folderPath)
{
if (!(folderPath.EndsWith(System.IO.Path.DirectorySeparatorChar) || folderPath.EndsWith(System.IO.Path.AltDirectorySeparatorChar)))
{
folderPath = Utilities.PathCombine(folderPath, "\\");
}
var path = WebUtility.UrlEncode(folderPath);
return await GetJsonAsync<Folder>($"{ApiUrl}/{siteId}/{path}");
}

View File

@ -1,4 +1,4 @@
using Oqtane.Models;
using Oqtane.Models;
using System.Threading.Tasks;
using System.Net.Http;
using Oqtane.Shared;
@ -25,5 +25,10 @@ namespace Oqtane.Services
{
return await GetJsonAsync<Installation>($"{ApiUrl}/upgrade");
}
public async Task RestartAsync()
{
await PostAsync($"{ApiUrl}/restart");
}
}
}

View File

@ -1,4 +1,4 @@
using Oqtane.Models;
using Oqtane.Models;
using System.Threading.Tasks;
using Oqtane.Shared;
@ -9,5 +9,6 @@ namespace Oqtane.Services
Task<Installation> IsInstalled();
Task<Installation> Install(InstallConfig config);
Task<Installation> Upgrade();
Task RestartAsync();
}
}

View File

@ -0,0 +1,17 @@
using Oqtane.Models;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Oqtane.Services
{
public interface ILanguageService
{
Task<List<Language>> GetLanguagesAsync(int siteId);
Task<Language> GetLanguageAsync(int languageId);
Task<Language> AddLanguageAsync(Language language);
Task DeleteLanguageAsync(int languageId);
}
}

View File

@ -0,0 +1,11 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Oqtane.Models;
namespace Oqtane.Services
{
public interface ILocalizationService
{
Task<IEnumerable<Culture>> GetCulturesAsync();
}
}

View File

@ -1,4 +1,4 @@
using Oqtane.Models;
using Oqtane.Models;
using Oqtane.UI;
using System.Collections.Generic;
using System.Threading.Tasks;
@ -12,6 +12,7 @@ namespace Oqtane.Services
Task UpdateModuleDefinitionAsync(ModuleDefinition moduleDefinition);
Task InstallModuleDefinitionsAsync();
Task DeleteModuleDefinitionAsync(int moduleDefinitionId, int siteId);
Task CreateModuleDefinitionAsync(ModuleDefinition moduleDefinition, int moduleId);
Task<ModuleDefinition> CreateModuleDefinitionAsync(ModuleDefinition moduleDefinition);
Task<List<string>> GetModuleDefinitionTemplatesAsync();
}
}

View File

@ -1,4 +1,4 @@
using Oqtane.Models;
using Oqtane.Models;
using System.Collections.Generic;
using System.Threading.Tasks;
@ -12,5 +12,7 @@ namespace Oqtane.Services
List<ThemeControl> GetContainerControls(List<Theme> themes, string themeName);
Task InstallThemesAsync();
Task DeleteThemeAsync(string themeName);
Task<Theme> CreateThemeAsync(Theme theme);
Task<List<string>> GetThemeTemplatesAsync();
}
}

View File

@ -1,4 +1,4 @@
using Oqtane.Models;
using Oqtane.Models;
using System.Threading.Tasks;
namespace Oqtane.Services
@ -13,7 +13,7 @@ namespace Oqtane.Services
Task<User> UpdateUserAsync(User user);
Task DeleteUserAsync(int userId);
Task DeleteUserAsync(int userId, int siteId);
Task<User> LoginUserAsync(User user, bool setCookie, bool isPersistent);

View File

@ -0,0 +1,38 @@
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Oqtane.Models;
using Oqtane.Shared;
namespace Oqtane.Services
{
public class LanguageService : ServiceBase, ILanguageService
{
private readonly SiteState _siteState;
public LanguageService(HttpClient http, SiteState siteState) : base(http)
{
_siteState = siteState;
}
private string Apiurl => CreateApiUrl(_siteState.Alias, "Language");
public async Task<List<Language>> GetLanguagesAsync(int siteId)
{
var languages = await GetJsonAsync<List<Language>>($"{Apiurl}?siteid={siteId}");
return languages?.OrderBy(l => l.Name).ToList() ?? Enumerable.Empty<Language>().ToList();
}
public async Task<Language> GetLanguageAsync(int languageId)
=> await GetJsonAsync<Language>($"{Apiurl}/{languageId}");
public async Task<Language> AddLanguageAsync(Language language)
=> await PostJsonAsync<Language>(Apiurl, language);
public async Task DeleteLanguageAsync(int languageId)
=> await DeleteAsync($"{Apiurl}/{languageId}");
}
}

View File

@ -0,0 +1,22 @@
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using Oqtane.Models;
using Oqtane.Shared;
namespace Oqtane.Services
{
public class LocalizationService : ServiceBase, ILocalizationService
{
private readonly SiteState _siteState;
public LocalizationService(HttpClient http, SiteState siteState) : base(http)
{
_siteState = siteState;
}
private string Apiurl => CreateApiUrl(_siteState.Alias, "Localization");
public async Task<IEnumerable<Culture>> GetCulturesAsync() => await GetJsonAsync<IEnumerable<Culture>>(Apiurl);
}
}

View File

@ -1,4 +1,4 @@
using Oqtane.Models;
using Oqtane.Models;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
@ -49,9 +49,15 @@ namespace Oqtane.Services
await DeleteAsync($"{Apiurl}/{moduleDefinitionId}?siteid={siteId}");
}
public async Task CreateModuleDefinitionAsync(ModuleDefinition moduleDefinition, int moduleId)
public async Task<ModuleDefinition> CreateModuleDefinitionAsync(ModuleDefinition moduleDefinition)
{
await PostJsonAsync($"{Apiurl}?moduleid={moduleId}", moduleDefinition);
return await PostJsonAsync($"{Apiurl}", moduleDefinition);
}
public async Task<List<string>> GetModuleDefinitionTemplatesAsync()
{
List<string> templates = await GetJsonAsync<List<string>>($"{Apiurl}/templates");
return templates;
}
}
}

View File

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

View File

@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
@ -29,10 +29,10 @@ namespace Oqtane.Services
return themes.SelectMany(item => item.Themes).ToList();
}
//[Obsolete("This method is deprecated.", false)]
public List<ThemeControl> GetLayoutControls(List<Theme> themes, string themeName)
{
return themes.Where(item => Utilities.GetTypeName(themeName).StartsWith(Utilities.GetTypeName(item.ThemeName)))
.SelectMany(item => item.Layouts).ToList();
return null;
}
public List<ThemeControl> GetContainerControls(List<Theme> themes, string themeName)
@ -50,5 +50,16 @@ namespace Oqtane.Services
{
await DeleteAsync($"{ApiUrl}/{themeName}");
}
public async Task<Theme> CreateThemeAsync(Theme theme)
{
return await PostJsonAsync($"{ApiUrl}", theme);
}
public async Task<List<string>> GetThemeTemplatesAsync()
{
List<string> templates = await GetJsonAsync<List<string>>($"{ApiUrl}/templates");
return templates;
}
}
}

View File

@ -1,4 +1,4 @@
using Oqtane.Shared;
using Oqtane.Shared;
using Oqtane.Models;
using System.Net.Http;
using System.Threading.Tasks;
@ -36,9 +36,9 @@ namespace Oqtane.Services
return await PutJsonAsync<User>($"{Apiurl}/{user.UserId}", user);
}
public async Task DeleteUserAsync(int userId)
public async Task DeleteUserAsync(int userId, int siteId)
{
await DeleteAsync($"{Apiurl}/{userId}");
await DeleteAsync($"{Apiurl}/{userId}?siteid={siteId}");
}
public async Task<User> LoginUserAsync(User user, bool setCookie, bool isPersistent)

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