Compare commits

...

1101 Commits

Author SHA1 Message Date
7683af81bc Update README.md 2025-03-12 14:22:43 -04:00
bad10b3812 6.1.1 Release
6.1.1 Release
2025-03-12 14:21:41 -04:00
9f9522c2ed 6.1.1 Release
6.1.1 Release
2025-03-12 14:21:15 -04:00
62879c3e52 Merge pull request #5162 from sbwalker/dev
upgrade to .NET 9.0.3
2025-03-11 14:36:14 -04:00
45610f8dd7 upgrade to .NET 9.0.3 2025-03-11 14:35:59 -04:00
18102cbd78 Merge pull request #5160 from sbwalker/dev
sort endpoints by route
2025-03-11 13:11:34 -04:00
262d6a1529 sort endpoints by route 2025-03-11 13:11:19 -04:00
1124ddaf90 Merge pull request #5157 from zyhfish/task/fix-5156
Fix #5156: update the bind event to oninput.
2025-03-11 11:51:38 -04:00
981add3872 Merge pull request #5158 from sbwalker/dev
rename Cache service to OutputCache
2025-03-11 11:48:58 -04:00
8d4b30140e rename Cache service to OutputCache 2025-03-11 11:48:43 -04:00
Ben
b9c59137a8 Fix #5156: update the bind event to oninput. 2025-03-11 22:58:06 +08:00
0b1c7e06ca Update README.md 2025-03-10 10:56:41 -04:00
fcaf80cba6 Update README.md 2025-03-10 10:56:11 -04:00
6358b9eabb Merge pull request #5155 from sbwalker/dev
added Logout Everywhere option to User Settings
2025-03-10 10:53:25 -04:00
70a3fab1ff added Logout Everywhere option to User Settings 2025-03-10 10:53:05 -04:00
bdf86ace86 Merge pull request #5152 from sbwalker/dev
upgrade to ImageSharp 3.1.7 due to security vulnerability
2025-03-07 14:17:02 -05:00
d57132d1e4 upgrade to ImageSharp 3.1.7 due to security vulnerability 2025-03-07 14:16:47 -05:00
a6e87abf99 Merge pull request #5151 from sbwalker/dev
allow site settings to be overidden at host level
2025-03-07 14:15:31 -05:00
f1771610fe allow site settings to be overidden at host level 2025-03-07 14:15:16 -05:00
a88ea9780f Update README.md 2025-03-06 15:45:30 -05:00
bca0866d72 Update README.md 2025-03-06 15:43:59 -05:00
cebed93abf Update README.md 2025-03-06 15:42:48 -05:00
ee2b2e3569 Update README.md 2025-03-06 15:40:07 -05:00
cb8e9ee244 Update README.md 2025-03-06 15:38:18 -05:00
486184b16c Update README.md 2025-03-06 15:36:28 -05:00
9f9bd1988f Merge pull request #5149 from sbwalker/dev
update based on changes suggested by @adefwebserver
2025-03-06 15:25:37 -05:00
ba1bfd1bc0 update based on changes suggested by @adefwebserver 2025-03-06 15:25:25 -05:00
e12926e971 Merge pull request #5148 from sbwalker/dev
allow page and module settings to be included in site templates, improve terms and privacy default content, add Settings for HtmlText module
2025-03-06 14:46:38 -05:00
5b4db0de3b allow page and module settings to be included in site templates, improve terms and privacy default content, add Settings for HtmlText module 2025-03-06 14:46:17 -05:00
70ff55faa6 Merge pull request #5147 from sbwalker/dev
modify terminology
2025-03-06 09:43:55 -05:00
f2bd47d8bc modify terminology 2025-03-06 09:43:40 -05:00
e2b9c9e98e Merge pull request #5146 from sbwalker/dev
prepare for 6.1.1
2025-03-05 15:02:39 -05:00
b0791a594f prepare for 6.1.1 2025-03-05 15:02:12 -05:00
ea5eaa6ed2 Merge pull request #5145 from sbwalker/dev
update to .NET 9.0.2
2025-03-05 14:55:00 -05:00
ec3fd1d585 update to .NET 9.0.2 2025-03-05 14:54:46 -05:00
d76de22977 Merge pull request #5144 from sbwalker/dev
modify localization text
2025-03-05 10:45:17 -05:00
c0e3483cc7 modify localization text 2025-03-05 10:44:34 -05:00
0994cdf3b6 Merge pull request #5141 from sbwalker/dev
sync interop.js changes with .NET MAUI
2025-03-04 16:25:24 -05:00
a76fd82262 sync interop.js changes with .NET MAUI 2025-03-04 16:25:10 -05:00
2f919c7d69 Merge pull request #5140 from sbwalker/dev
fix regression issue with Search component
2025-03-04 15:45:16 -05:00
f12592731b fix regression issue with Search component 2025-03-04 15:45:02 -05:00
4a20e1a25d Merge pull request #5138 from zyhfish/task/improve-middle-screen-view
improve the styles in middle screen size.
2025-03-04 15:42:37 -05:00
cc7111c3ff Merge pull request #5139 from sbwalker/dev
change module title for Terms
2025-03-04 15:40:58 -05:00
81972aed62 change module title for Terms 2025-03-04 15:40:44 -05:00
Ben
5e2092c6d4 improve the styles in middle screen size. 2025-03-04 23:31:21 +08:00
d136f8ac91 Merge pull request #5137 from sbwalker/dev
add nonce support
2025-03-03 16:49:41 -05:00
5b23917940 add nonce support 2025-03-03 16:49:28 -05:00
2cda0a3798 Merge pull request #5136 from sbwalker/dev
modify cookie consent text
2025-03-03 15:37:46 -05:00
f315ad1ce9 modify cookie consent text 2025-03-03 15:37:31 -05:00
49f1c273c2 Merge pull request #5134 from sbwalker/dev
add terms to upgrademanager
2025-03-03 13:36:47 -05:00
8518476c87 add terms to upgrademanager 2025-03-03 13:36:32 -05:00
a34ed756db Merge pull request #5132 from mdmontesinos/sitemap-cache
resolves #4899: output cache for sitemap
2025-03-03 13:22:46 -05:00
a48232c4e3 Merge pull request #5133 from sbwalker/dev
add default privacy and terms
2025-03-03 13:22:33 -05:00
ac65e38390 add default privacy and terms 2025-03-03 13:22:17 -05:00
eab3a753f5 resolves #4899: output cache for sitemap 2025-03-03 17:54:33 +01:00
9e5922e121 Merge pull request #5130 from zyhfish/task/fix-4936
Fix #4936: set the allow cookie value when refresh state.
2025-03-03 07:57:42 -05:00
Ben
bf57b23776 update the page state after policy changed. 2025-03-01 15:05:23 +08:00
Ben
8f4a20fd46 Fix #4936: set the allow cookie value when refresh state. 2025-03-01 14:16:42 +08:00
b3716da5ac Merge pull request #5128 from zyhfish/task/fix-4936-new
move the styles to app.css.
2025-02-28 13:36:58 -05:00
2c129fd800 Merge pull request #5129 from sbwalker/dev
localization text change
2025-02-28 13:36:49 -05:00
6fb18e7a25 localization text change 2025-02-28 13:36:34 -05:00
Ben
38d28d6944 move the styles to app.css. 2025-02-28 23:53:53 +08:00
a187e1a7a2 Merge pull request #5127 from sbwalker/dev
fix RESX issue
2025-02-28 10:52:18 -05:00
7d7a19c7c2 fix RESX issue 2025-02-28 10:52:06 -05:00
6a2ae2153a Merge pull request #5119 from zyhfish/task/fix-4936
update the cookie consent control.
2025-02-28 10:48:21 -05:00
f50ba1a91e Merge branch 'dev' into task/fix-4936 2025-02-28 10:47:24 -05:00
b09575dbd6 Merge pull request #5126 from sbwalker/dev
provide option to assign a theme to a site
2025-02-28 10:45:40 -05:00
c52ee3d91d provide option to assign a theme to a site 2025-02-28 10:45:25 -05:00
Ben
1ced5c0425 update the cookie consent control. 2025-02-28 23:32:17 +08:00
e399a5c9b1 Merge pull request #5124 from sbwalker/dev
add missing maxlength attributes
2025-02-27 10:51:37 -05:00
08dff5fb67 add missing maxlength attributes 2025-02-27 10:51:22 -05:00
912760f2a7 Update SECURITY.md 2025-02-26 15:29:49 -05:00
4b62fdbf93 Update SECURITY.md 2025-02-26 15:27:33 -05:00
df593d43a7 Update SECURITY.md 2025-02-26 15:25:25 -05:00
89b1fba771 Merge pull request #5123 from sbwalker/dev
remove package validation logic
2025-02-26 12:54:06 -05:00
5505c91ae0 remove package validation logic 2025-02-26 12:53:53 -05:00
cc720ff399 Merge pull request #5122 from sbwalker/dev
remove unnecessary log message
2025-02-26 12:12:14 -05:00
29f07f6c56 remove unnecessary log message 2025-02-26 12:11:56 -05:00
a69e197a1f Merge pull request #5121 from zyhfish/task/fix-5116
Fix #5116: parse the value as UTC time.
2025-02-26 07:45:55 -05:00
Ben
6dddd8eff8 only allow essential cookies when cookie consent not granted. 2025-02-26 19:41:58 +08:00
Ben
51aada8922 Fix #5116: parse the value as UTC time. 2025-02-26 18:25:24 +08:00
Ben
b47bf40e8f update the cookie consent control. 2025-02-25 11:36:47 +08:00
48151bf365 Merge pull request #5118 from sbwalker/dev
remove IJSRuntime reference as it was causing a compilation warning
2025-02-24 16:15:51 -05:00
659950996d remove IJSRuntime reference as it was causing a compilation warning 2025-02-24 16:15:35 -05:00
6e656a4d0a Merge pull request #5114 from zyhfish/task/fix-4936
Fix #4936: add the cookie consent theme control.
2025-02-24 16:08:22 -05:00
Ben
bf308dd13d enable child component of cookie consent control. 2025-02-24 22:32:19 +08:00
Ben
982f3b1943 Fix #4936: add the cookie consent theme control. 2025-02-22 09:49:33 +08:00
7a4ea8cf1b Merge pull request #5094 from zyhfish/task/set-fa-ir-culture
Fix #5054: resolve the issue in fa-IR language.
2025-02-19 07:36:16 -05:00
7c0482a87c Merge pull request #5111 from sbwalker/dev
remove warning message related to no jobs being registered
2025-02-18 11:50:57 -05:00
101ededd89 remove warning message related to no jobs being registered 2025-02-18 11:50:36 -05:00
a8cbc0040e Merge pull request #5108 from zyhfish/task/fix-5103
Fix #5103: return a copy of the assembly list.
2025-02-18 11:40:30 -05:00
ed91bb445b Merge pull request #5110 from sbwalker/dev
improve HostedServiceBase so that scheduled jobs can be registered during installation
2025-02-18 11:36:00 -05:00
f158a222f4 improve HostedServiceBase so that scheduled jobs can be registered during installation 2025-02-18 11:35:38 -05:00
46bcad1fca Merge pull request #5109 from sbwalker/dev
clean up scheduled jobs which have been uninstalled
2025-02-18 09:12:48 -05:00
5e147afb9f clean up scheduled jobs which have been uninstalled 2025-02-18 09:12:26 -05:00
Ben
b061d4593f Fix #5103: return a copy of the assembly list. 2025-02-18 22:02:25 +08:00
3fa520b4ef Merge pull request #5107 from sbwalker/dev
make purge job output more readable
2025-02-18 08:53:38 -05:00
2df05b4afd make purge job output more readable 2025-02-18 08:53:23 -05:00
e0569a6748 Merge pull request #5104 from sbwalker/dev
fix visitor purge logic
2025-02-17 13:22:11 -05:00
2e6ab398d9 fix visitor purge logic 2025-02-17 13:21:58 -05:00
Ben
94b03d2a6b update settings for all RTL languages. 2025-02-16 10:25:43 +08:00
f84fe30bb6 Merge pull request #5097 from sbwalker/dev
synchronize BlazorScriptReload changes
2025-02-14 09:17:58 -05:00
049ddef531 synchronize BlazorScriptReload changes 2025-02-14 09:17:44 -05:00
a1a214c742 Merge pull request #5096 from sbwalker/dev
add another constructor for Script class
2025-02-14 09:09:14 -05:00
c40a483ffa add another constructor for Script class 2025-02-14 09:09:00 -05:00
Ben
aff99acfae Fix #5054: resolve the issue in fa-IR language. 2025-02-12 20:18:10 +08:00
628129c08d Update README.md 2025-02-11 12:00:41 -05:00
679d34dfdf Merge pull request #5093 from oqtane/master
6.1.0 Release
2025-02-11 11:49:29 -05:00
b2f65903ae Merge pull request #5092 from oqtane/dev
6.1.0 Release
2025-02-11 11:49:06 -05:00
2daefe0382 Merge pull request #5091 from tvatavuk/patch-1
Minor fix in ThemeController.cs
2025-02-11 11:41:52 -05:00
1214a11704 Minor fix in ThemeController.cs
update `SharedReference` to use "Oqtane.Shared" for PackageReference code
2025-02-11 14:35:13 +01:00
e55e0118c2 Merge pull request #5090 from sbwalker/dev
fix #5089 - remove upgrade cleanup logic as .NET 9.0.1 moves assemblies back to /bin folder
2025-02-10 16:53:42 -05:00
a1ac81e907 fix #5089 - remove upgrade cleanup logic as .NET 9.0.1 moves assemblies back to /bin folder 2025-02-10 16:53:22 -05:00
14ad68bf69 Merge pull request #5088 from sbwalker/dev
modify RemoveAssemblies method so that it only runs once - not for every tenant
2025-02-10 16:27:24 -05:00
e3118c6e99 modify RemoveAssemblies method so that it only runs once - not for every tenant 2025-02-10 16:27:05 -05:00
b41aeab8f8 Merge pull request #5087 from sbwalker/dev
update Provider property to Pomelo
2025-02-10 10:47:07 -05:00
1a738b358e update Provider property to Pomelo 2025-02-10 10:46:44 -05:00
f4b1e8035b Merge pull request #5086 from sbwalker/dev
improve notification add and update methods
2025-02-10 09:50:35 -05:00
324e985247 improve notification add and update methods 2025-02-10 09:50:21 -05:00
60faacd7d0 Merge pull request #5085 from sbwalker/dev
fix comment
2025-02-10 08:21:13 -05:00
d4a4d7c346 fix comment 2025-02-10 08:20:58 -05:00
189f8f1d27 Merge pull request #5082 from sbwalker/dev
enhance purge job to trim broken urls based on retention policy
2025-02-09 13:02:21 -05:00
ed353461da enhance purge job to trim broken urls based on retention policy 2025-02-09 13:02:07 -05:00
e9330d6c62 Merge pull request #5080 from zyhfish/task/fix-5079
Fix #5079: Retrieve all Url Mapping records
2025-02-09 12:35:28 -05:00
f53e7cc3f6 Merge pull request #5081 from sbwalker/dev
fix #5072 - administrators should be allowed to send system notifications
2025-02-09 12:34:38 -05:00
4f4258d532 fix #5072 - administrators should be allowed to send system notifications 2025-02-09 12:34:17 -05:00
Ben
c80910f355 Fix #5079: remove the records limit. 2025-02-08 11:51:38 +08:00
12470ab178 Merge pull request #5075 from mdmontesinos/dev
fix #5074: generate cancellation token for file upload
2025-02-07 07:53:16 -05:00
704e091e9b Merge pull request #5076 from sbwalker/dev
synchronize interop script changes with .NET MAUI
2025-02-07 07:52:58 -05:00
f30f1e5c1f synchronize interop script changes with .NET MAUI 2025-02-07 07:52:43 -05:00
0741ce2197 fix #5074: generate cancellation token for file upload 2025-02-07 09:11:52 +01:00
fc81bae9b7 Update README.md 2025-02-06 15:16:50 -05:00
3fa68e4f96 Merge pull request #5071 from sbwalker/dev
moved file setting to File Management and added Max Chunk Size
2025-02-06 15:10:29 -05:00
05a767c7be moved file setting to File Management and added Max Chunk Size 2025-02-06 15:10:14 -05:00
8c1e8f6377 Merge pull request #5070 from sbwalker/dev
fix #5067 - add support for Guid data types
2025-02-06 14:17:06 -05:00
0fbae8d7da fix #5067 - add support for Guid data types 2025-02-06 14:16:53 -05:00
cec4b339f5 Merge pull request #5069 from mdmontesinos/dev-test
fix #5058: ensure sequential file and chunk uploads to avoid overload
2025-02-06 13:56:35 -05:00
1a7656d8ee fix #5058: ensure sequential file and chunk uploads to avoid overload 2025-02-06 19:21:51 +01:00
e173815810 Merge pull request #5068 from sbwalker/dev
fix LogLevel for file upload error
2025-02-06 12:21:51 -05:00
620c768e05 fix LogLevel for file upload error 2025-02-06 12:21:34 -05:00
7740679077 Merge pull request #5066 from sbwalker/dev
fix #5061 - configure Page Management for personalizable pages
2025-02-06 11:16:46 -05:00
1ebc8ebff3 fix #5061 - configure Page Management for personalizable pages 2025-02-06 11:16:30 -05:00
fa4fac70d5 Merge pull request #5065 from sbwalker/dev
improve file upload validation and error handling on server
2025-02-06 08:20:13 -05:00
8c83a18f93 improve file upload validation and error handling on server 2025-02-06 08:19:57 -05:00
a151ecfda0 Merge pull request #5064 from sbwalker/dev
modified file upload error message to reflect new behavior
2025-02-05 19:10:10 -05:00
dec0c0649c modified file upload error message to reflect new behavior 2025-02-05 19:09:55 -05:00
a356f893ac Merge pull request #5063 from sbwalker/dev
remove uploadFile() method as it is not used
2025-02-05 17:08:56 -05:00
e2af4f74c3 remove uploadFile() method as it is not used 2025-02-05 17:08:32 -05:00
99022b76e5 Merge pull request #5062 from sbwalker/dev
file upload improvements
2025-02-05 16:48:59 -05:00
9dd6dc7523 file upload improvements 2025-02-05 16:48:34 -05:00
6f588200d7 Merge pull request #5057 from sbwalker/dev
fix #5044 - improve file part removal logic
2025-02-03 11:00:37 -05:00
f3dbeae28e fix #5044 - improve file part removal logic 2025-02-03 11:00:22 -05:00
9f70361298 Merge pull request #5056 from sbwalker/dev
add additional Script class constructors
2025-02-03 10:35:32 -05:00
534353ce13 add additional Script class constructors 2025-02-03 10:35:17 -05:00
3f391a7354 Merge pull request #5052 from sbwalker/dev
modify button spacing
2025-01-31 14:51:06 -05:00
0dd0752710 modify button spacing 2025-01-31 14:50:54 -05:00
710fae4b0e Merge pull request #5051 from sbwalker/dev
add user impersonation
2025-01-31 11:14:28 -05:00
de6c57a7ee add user impersonation 2025-01-31 11:14:13 -05:00
7eee1fcd6a Merge pull request #5050 from sbwalker/dev
make Kestrel the default profile in launchjSettings.json
2025-01-31 09:18:59 -05:00
1fd2aedf96 make Kestrel the default profile in launchjSettings.json 2025-01-31 09:18:44 -05:00
ffdd7c063b Merge pull request #5049 from sbwalker/dev
added a ScriptsLoaded property in ModuleBase and ThemeBase for flow control in Interactive rendering scenarios
2025-01-31 08:42:56 -05:00
a87af264eb added a ScriptsLoaded property in ModuleBase and ThemeBase for flow control in Interactive rendering scenarios 2025-01-31 08:42:36 -05:00
3640cd2fdd Merge pull request #5046 from sbwalker/dev
fix upgrade issue which can occur in development environments
2025-01-30 08:40:08 -05:00
f7cf25c4bb fix upgrade issue which can occur in development environments 2025-01-30 08:39:49 -05:00
77dbd0d4c7 Merge pull request #5045 from sbwalker/dev
update version to 6.1.0
2025-01-30 08:08:34 -05:00
5a77c83e68 update version to 6.1.0 2025-01-30 08:08:16 -05:00
0a6763e08c Merge pull request #5043 from sbwalker/dev
change ResourceLoadBehavior Never to None
2025-01-29 19:05:20 -05:00
b5aa206670 change ResourceLoadBehavior Never to None 2025-01-29 19:05:05 -05:00
dbb4d9b64b Merge pull request #5042 from sbwalker/dev
fix logic to retrieve access token
2025-01-29 16:03:11 -05:00
6775edfd66 fix logic to retrieve access token 2025-01-29 16:02:55 -05:00
b06750ed65 Merge pull request #5040 from sbwalker/dev
remove method which was relocated to PageRepository
2025-01-29 13:12:59 -05:00
57879c1891 remove method which was relocated to PageRepository 2025-01-29 13:12:46 -05:00
65b55a76f2 Merge pull request #5039 from sbwalker/dev
improve asset caching help text
2025-01-29 12:40:02 -05:00
8562a68306 improve asset caching help text 2025-01-29 12:39:47 -05:00
160da46b5a Merge pull request #5038 from sbwalker/dev
remove Environment.IsDevelopment logic
2025-01-29 12:27:57 -05:00
ae5f70a739 remove Environment.IsDevelopment logic 2025-01-29 12:27:42 -05:00
4456e57466 Merge pull request #5036 from sbwalker/dev
use fingerprint term consistently
2025-01-29 10:42:34 -05:00
24cd090c61 use fingerprint term consistently 2025-01-29 10:42:19 -05:00
4f849f5d5f Merge pull request #5035 from sbwalker/dev
performance improvement when loading files within a folder
2025-01-29 10:23:11 -05:00
db2e86e84c performance improvement when loading files within a folder 2025-01-29 10:22:56 -05:00
14748ce2b3 Merge pull request #5034 from sbwalker/dev
use Configuration service as it already exists
2025-01-29 08:28:08 -05:00
527509732c use Configuration service as it already exists 2025-01-29 08:22:21 -05:00
aa9214477c Merge pull request #5033 from sbwalker/dev
fix #5018 - redirect file download to login page
2025-01-28 16:31:03 -05:00
db24ed8b55 fix #5018 - redirect file download to login page 2025-01-28 16:30:49 -05:00
349d1849d9 Merge pull request #5032 from sbwalker/dev
use deterministic hash in file server image generation
2025-01-28 15:55:56 -05:00
188be2fa8c use deterministic hash in file server image generation 2025-01-28 15:55:40 -05:00
5c4d7df734 Merge pull request #5031 from sbwalker/dev
remove Oqtane.Server.staticwebassets.endpoints.json from release packages
2025-01-28 14:39:48 -05:00
37de18c670 remove Oqtane.Server.staticwebassets.endpoints.json from release packages 2025-01-28 14:39:30 -05:00
0001e3844b Merge pull request #5030 from sbwalker/dev
provides options to control caching for static assets
2025-01-28 14:30:13 -05:00
65f171f701 provides options to control caching for static assets 2025-01-28 14:29:58 -05:00
c4308c239c Merge pull request #5007 from RahulKaushik007/fix-static-file-caching
Fixes #5005: Add Browser Caching for Static Assets
2025-01-28 13:21:29 -05:00
2b6af3cb37 Merge pull request #5029 from sbwalker/dev
fix localization text
2025-01-28 13:18:32 -05:00
c5a16fbbc1 fix localization text 2025-01-28 13:18:18 -05:00
1db83f509b Merge pull request #5028 from sbwalker/dev
add caching support for folders
2025-01-28 12:47:37 -05:00
2a06304a2c add caching support for folders 2025-01-28 12:47:23 -05:00
7bbe684135 Merge pull request #5027 from sbwalker/dev
improve terminology consistency
2025-01-28 08:56:19 -05:00
a996a88fc4 improve terminology consistency 2025-01-28 08:56:05 -05:00
8cf9e7db51 Merge pull request #5026 from sbwalker/dev
add Fingerprint property to ModuleBase and ThemeBase
2025-01-28 08:44:01 -05:00
ed981c67b7 add Fingerprint property to ModuleBase and ThemeBase 2025-01-28 08:43:48 -05:00
6a77a0a5b9 Merge pull request #5025 from sbwalker/dev
improve terminology
2025-01-28 08:34:31 -05:00
bfb4b4431b improve terminology 2025-01-28 08:34:15 -05:00
3de44c0335 Merge pull request #5024 from sbwalker/dev
add ThemeState property to ThemeBase
2025-01-28 08:27:25 -05:00
37afd1aec9 add ThemeState property to ThemeBase 2025-01-28 08:27:10 -05:00
b2ac561673 Merge pull request #5021 from thabaum/update-package-dependencies-9.0.1
Fixes #5020: Updates package dependencies 9.0.1 (Latest)
2025-01-27 16:44:33 -05:00
c9bf7d9138 Merge pull request #5022 from sbwalker/dev
fix #5005 - adds versioning (ie. fingerprinting) for static assets - core, modules, and themes.
2025-01-27 16:35:07 -05:00
153a689bdb fix #5005 - adds versioning (ie. fingerprinting) for static assets - core, modules, and themes. 2025-01-27 16:34:47 -05:00
26b88f1a22 Update Package Dependencies to 9.0.1 2025-01-27 05:13:31 -08:00
c66a5d028f Update Package Dependencies to 9.0.1 2025-01-27 05:13:01 -08:00
8441c95a5c Update Package Dependencies to 9.0.1 2025-01-27 05:11:49 -08:00
e0e32b0199 Update Package Dependencies to 9.0.1 and 9.0.30 2025-01-27 05:09:19 -08:00
2bb76564e9 Update Package Dependencies to 9.0.1 2025-01-27 05:06:46 -08:00
86ec25d4de Update Package Dependencies to 9.0.1 2025-01-27 05:05:42 -08:00
ed9929963c Update Package Dependencies to 9.0.1 2025-01-27 05:04:34 -08:00
36f50118ac Update Package Dependencies to 9.0.1 2025-01-27 05:03:46 -08:00
72ddf27504 Update Package Dependency to 9.2.0 2025-01-27 05:03:03 -08:00
9bd36931ff Update Package Dependencies to 9.0.1 2025-01-27 05:02:10 -08:00
056ef7a3d5 Update Package Dependencies to 9.0.1 2025-01-27 05:00:08 -08:00
e483945d05 Merge pull request #5017 from sbwalker/dev
fix #5014 - page content scripts not loading on initial page request in Interactive rendering
2025-01-24 14:29:42 -05:00
7a9c637e03 fix #5014 - page content scripts not loading on initial page request in Interactive rendering 2025-01-24 14:29:23 -05:00
09ce543ea6 Merge pull request #5013 from sbwalker/dev
allow packages to be managed across installations
2025-01-23 09:08:16 -05:00
0ef24ebc3f allow packages to be managed across installations 2025-01-23 09:08:02 -05:00
a4419d3af6 Merge pull request #5011 from sbwalker/dev
remove GetButtonSize method
2025-01-22 07:43:38 -05:00
935983c02a remove GetButtonSize method 2025-01-22 07:43:23 -05:00
bd54ce5017 Merge pull request #4998 from leigh-pointer/ActionDialogSize
Update ActionDialog Add method to ensure consistent button sizing
2025-01-22 07:42:04 -05:00
af6ed78b8e Update ActionDialog.razor 2025-01-22 07:57:52 +01:00
f72438996d Merge remote-tracking branch 'upstream/dev' into ActionDialogSize 2025-01-22 07:36:01 +01:00
9db2a55a5a Merge pull request #5010 from sbwalker/dev
fix #4964 - use bearer token if it already exists
2025-01-21 16:55:19 -05:00
950d90badb fix #4964 - use bearer token if it already exists 2025-01-21 16:55:02 -05:00
1864d180af Merge pull request #5003 from sdi2121/patch-1
Update Oqtane.Server.csproj - MySQL deploy to Azure error
2025-01-21 15:58:44 -05:00
0e82e98382 Merge pull request #5006 from mdmontesinos/patch-1
FIX: File server MimeType not updated after image conversion
2025-01-21 15:58:33 -05:00
46023d35dc Merge pull request #5009 from sbwalker/dev
fix #4976 - manage hierarchical path updates and page deletion
2025-01-21 15:58:08 -05:00
90d2e0a40b fix #4976 - manage hierarchical path updates and page deletion 2025-01-21 15:57:48 -05:00
5f884e0796 Merge pull request #5008 from sbwalker/dev
fix #4965 - improve user/site management
2025-01-21 12:21:50 -05:00
16477052e2 fix #4965 - improve user/site management 2025-01-21 12:21:27 -05:00
66a05603f7 Fix static file caching headers 2025-01-21 17:42:08 +05:30
fe2a883386 Fix static file caching headers 2025-01-21 17:26:47 +05:30
ca7fdaa125 FIX: File server MimeType not updated after image conversion 2025-01-20 12:17:22 +01:00
1283ec2008 Update Oqtane.Server.csproj
Fixes Azure manual deployment build from local code build. May need an additional fix in MySQL library (Line 24 - MySQLDatabase.cs)
2025-01-18 13:53:04 -05:00
d1f78f9048 Update LICENSE 2025-01-17 12:57:32 -05:00
45f43bfade Merge pull request #5002 from sbwalker/dev
update copyright year
2025-01-17 12:57:14 -05:00
4793ab4bc9 update copyright year 2025-01-17 12:56:59 -05:00
88b174dea8 Merge pull request #5001 from sbwalker/dev
remove unused method
2025-01-17 12:50:42 -05:00
06ca382bd7 remove unused method 2025-01-17 12:50:26 -05:00
b09175a8db Merge pull request #5000 from sbwalker/dev
allow entry of name during installation
2025-01-17 11:14:49 -05:00
677f68b08d allow entry of name during installation 2025-01-17 11:14:35 -05:00
8058b8dba4 Merge pull request #4999 from sbwalker/dev
improve error messages
2025-01-17 07:54:49 -05:00
4bc26f13c1 improve error messages 2025-01-17 07:54:34 -05:00
e6cf77e724 Update ActionDialog Add method to ensure consistent button sizing
This PR introduces a new private method GetButtonSize() to enhance the consistency of button sizing within our UI. The method specifically checks for the presence of the "btn-sm" class in the Action Button's class list and applies the same sizing to the Cancel Button if found.
2025-01-17 13:34:16 +01:00
f8737c112e Merge pull request #4997 from sbwalker/dev
reload the script if data-reload is "always" or if the script has not been loaded previously and data-reload is "once" or "true"
2025-01-16 15:06:36 -05:00
74b72ed9d4 reload the script if data-reload is "always" or if the script has not been loaded previously and data-reload is "once" or "true" 2025-01-16 15:06:15 -05:00
4950391201 Merge pull request #4996 from sbwalker/dev
allow data-reload to support true or always
2025-01-16 14:22:06 -05:00
64a38d6e45 allow data-reload to support true or always 2025-01-16 14:21:52 -05:00
e2d8ee53f8 Merge pull request #4995 from sbwalker/dev
script reload improvements
2025-01-16 14:06:28 -05:00
0204ff8dd5 script reload improvements 2025-01-16 14:06:13 -05:00
4630ee6e93 Merge pull request #4992 from beolafsen/dev
Trim ModuleOwner and ModuleName before create
2025-01-16 12:29:00 -05:00
c4f2abf143 Merge pull request #4994 from sbwalker/dev
fix #4986 - allow Resources which have Reload specified to be used in Interactive rendering
2025-01-16 12:26:30 -05:00
e7444a0194 fix #4986 - allow Resources which have Reload specified to be used in Interactive rendering 2025-01-16 12:26:10 -05:00
ffed7305ed Merge pull request #4993 from sbwalker/dev
fix #4984 - path mapping for personalized pages
2025-01-16 09:25:43 -05:00
334054bcd4 fix #4984 - path mapping for personalized pages 2025-01-16 09:25:27 -05:00
5bb98eb5b2 Trim ModuleOwner and ModuleName before create 2025-01-16 09:32:18 +01:00
e842bd882a Merge pull request #4991 from sbwalker/dev
fix #4984 - redirect not working for personalized pages
2025-01-15 11:57:00 -05:00
74bfb46f73 fix #4984 - redirect not working for personalized pages 2025-01-15 11:56:44 -05:00
cd45bf4b70 Merge pull request #4989 from sbwalker/dev
fix #4984 - ensure personalized page path does not contain illegal characters
2025-01-14 15:43:00 -05:00
51600bbcb0 fix #4984 - ensure personalized page path does not contain illegal characters 2025-01-14 15:42:40 -05:00
8811a9bcaa Merge pull request #4988 from sbwalker/dev
introduce RemoveAssemblies() method in UpgradeManager
2025-01-14 08:43:38 -05:00
4521f8a774 introduce RemoveAssemblies() method in UpgradeManager 2025-01-14 08:43:23 -05:00
5b427783f8 Merge pull request #4987 from zyhfish/task/fix-4954
Fix #4954: use Pomelo.EntityFrameworkCore.MySql package.
2025-01-14 08:34:34 -05:00
Ben
9508983b15 Fix #4954: use Pomelo.EntityFrameworkCore.MySql package. 2025-01-14 19:58:56 +08:00
fd09912cd7 Merge pull request #4978 from beolafsen/dev
Issue #4977
2025-01-13 16:00:06 -05:00
01cc8584b6 Merge pull request #4968 from leigh-pointer/unused
Removed unused Using statements from the SiteTemplates
2025-01-13 15:56:39 -05:00
c0b104e7c8 Merge pull request #4962 from thabaum/prepare-6.0.2-update-dependencies
Fixes #4961 - Updates Oqtane version to 6.0.2 and update related project dependencies to latest.
2025-01-13 15:55:13 -05:00
9a82021a82 Merge pull request #4983 from sbwalker/dev
include option for external login to save tokens
2025-01-13 15:14:27 -05:00
1fb54a0b0f include option for external login to save tokens 2025-01-13 15:14:13 -05:00
aa5ea61638 Merge pull request #4982 from sbwalker/dev
improve filtering logic in UserRole API
2025-01-13 14:42:36 -05:00
a59ec0258b improve filtering logic in UserRole API 2025-01-13 14:42:19 -05:00
b403f5cf71 Merge pull request #4980 from sbwalker/dev
fix comment spelling
2025-01-13 07:50:21 -05:00
0ac6a62b86 fix comment spelling 2025-01-13 07:50:05 -05:00
ed3743d3b6 Merge pull request #4979 from sbwalker/dev
fix #4969 - improve feedback and flow when connection string points to an invalid database
2025-01-13 07:48:50 -05:00
3468cba000 fix #4969 - improve feedback and flow when connection string points to an invalid database 2025-01-13 07:48:30 -05:00
5a4cdc5354 Issue #4977 2025-01-13 07:31:35 +01:00
af4e19a57e Removed unused Using statements from the SiteTemplates 2025-01-07 04:14:31 +01:00
26bb743679 Prepare 6.0.2 2024-12-31 10:37:27 -08:00
96cc726e22 Prepare 6.0.2 2024-12-31 10:35:00 -08:00
f4b00b01d0 Prepare 6.0.2 2024-12-31 10:34:21 -08:00
127b2ca86d Prepare 6.0.2 2024-12-31 10:32:09 -08:00
4b8b93e1b8 Prepare 6.0.2 2024-12-31 10:31:47 -08:00
3aea412fe9 Prepare 6.0.2 2024-12-31 10:31:23 -08:00
2aef96ad4f Prepare 6.0.2 2024-12-31 10:29:52 -08:00
ec0a77230c Prepare 6.0.2 2024-12-31 10:29:32 -08:00
b35e4bddd0 Prepare 6.0.2 2024-12-31 10:28:48 -08:00
aa32beb341 Update Oqtane.Shared.csproj 2024-12-31 10:28:32 -08:00
efafe89b42 Prepare 6.0.2 2024-12-31 10:26:45 -08:00
5ef2e49d9c Prepare 6.0.2 2024-12-31 10:25:34 -08:00
1cfbf61a30 Prepare 6.0.2 2024-12-31 10:23:15 -08:00
2bb5494b84 Prepare 6.0.2 2024-12-31 10:22:10 -08:00
e8a41ccb47 Prepare 6.0.2 2024-12-31 10:21:10 -08:00
7184f7f635 Prepare 6.0.2 and update package dependencies 2024-12-31 10:20:13 -08:00
cc5727b7fa Prepare 6.0.2 and update package dependencies 2024-12-31 10:16:35 -08:00
7f3d6ef6a5 Merge pull request #4959 from sbwalker/dev
fix #4957 - unable to login after password reset
2024-12-31 08:18:14 -05:00
44ce68097b fix #4957 - unable to login after password reset 2024-12-31 08:17:58 -05:00
d976cc6c19 Update SECURITY.md 2024-12-25 10:04:25 -05:00
d19d7d2a43 Merge pull request #4948 from zyhfish/task/fix-4947
Fix #4947: check the 2FA settings.
2024-12-24 08:46:43 -05:00
9bfaa02f97 Merge pull request #4953 from sbwalker/dev
add back System.Text.Json to Shared project (#4929)
2024-12-23 14:48:10 -05:00
2d9396b245 add back System.Text.Json to Shared project (#4929) 2024-12-23 14:47:17 -05:00
56e0da64ee Merge pull request #4952 from sbwalker/dev
updated default module template to use Service consistently
2024-12-23 14:10:07 -05:00
997e9213f2 updated default module template to use Service consistently 2024-12-23 14:09:54 -05:00
366569a23b Merge pull request #4951 from sbwalker/dev
update package references
2024-12-23 11:24:38 -05:00
36d5747b4f update package references 2024-12-23 11:24:24 -05:00
ea026c726c Merge pull request #4950 from sbwalker/dev
remove unnecessary reference to System.Text.Json in Shared project
2024-12-23 11:16:49 -05:00
1e71e32c74 remove unnecessary reference to System.Text.Json in Shared project 2024-12-23 11:16:34 -05:00
ed729bbd4f Merge pull request #4949 from sbwalker/dev
fix #4946 - allow administrators to access user roles via API
2024-12-23 09:27:04 -05:00
1a925221b7 fix #4946 - allow administrators to access user roles via API 2024-12-23 09:26:50 -05:00
Ben
af7b4db062 Fix #4947: check the 2FA settings. 2024-12-23 22:10:51 +08:00
cfefe35e3f Update README.md 2024-12-20 16:56:05 -05:00
8d7845a44d Merge pull request #4944 from oqtane/master
6.0.1 Release
2024-12-20 16:52:31 -05:00
3a15e6e5e9 Merge pull request #4943 from oqtane/dev
6.0.1 Release
2024-12-20 16:52:10 -05:00
3b8a51e855 Merge pull request #4942 from sbwalker/dev
fix reload script to use static array rather than a live HtmlCollection
2024-12-20 15:39:50 -05:00
f2cb34cc35 fix reload script to use static array rather than a live HtmlCollection 2024-12-20 15:39:35 -05:00
723ce62a34 Merge pull request #4940 from sbwalker/dev
write upgrade errors to log rather than console
2024-12-20 14:38:34 -05:00
2c9a2ea021 write upgrade errors to log rather than console 2024-12-20 14:38:21 -05:00
2be008d6d1 Merge pull request #4939 from sbwalker/dev
fix compilation issues in PR #4782
2024-12-20 12:36:24 -05:00
7fb51bdd0a fix compilation issues in PR #4782 2024-12-20 12:36:10 -05:00
abdbe3694f Merge pull request #4782 from thabaum/edit-add-page-scrolltotoppage-error
Fixes #4781: Adds Edit + Add Page ScrollToTopPage() On Settings Tab Form Error Messages
2024-12-20 12:26:55 -05:00
bd87e5012f Merge pull request #4938 from sbwalker/dev
fix documentation
2024-12-20 11:55:50 -05:00
55e18f2364 fix documentation 2024-12-20 11:55:35 -05:00
655e84072d Merge pull request #4937 from sbwalker/dev
use CompressionEnabled switch to disable static asset compression during publish - eliminates need to cleanup files manually in release.cmd
2024-12-20 11:46:52 -05:00
ab5409d5b6 use CompressionEnabled switch to disable static asset compression during publish - eliminates need to cleanup files manually in release.cmd 2024-12-20 11:46:30 -05:00
5a5da6486c Merge pull request #4933 from sbwalker/dev
remove *.br, *.gz files from wwwroot content folders in published release (as MapStaticAssets is not enabled)
2024-12-19 19:56:36 -05:00
7e99252429 remove *.br, *.gz files from wwwroot content folders in published release (as MapStaticAssets is not enabled) 2024-12-19 19:56:13 -05:00
10e0dcef8b Merge pull request #4931 from sbwalker/dev
ensure Pages collection is always returned in the same order by moving GetPagesHierarchy method to the repository
2024-12-19 14:45:34 -05:00
80c8433aad ensure Pages collection is always returned in the same order by moving GetPagesHierarchy method to the repository. 2024-12-19 14:45:12 -05:00
5b0ae372f8 Merge pull request #4930 from sbwalker/dev
add support for data-reload=false attribute in Interactive rendering
2024-12-19 13:04:59 -05:00
b5a1b529ab add support for data-reload=false attribute in Interactive rendering 2024-12-19 13:04:43 -05:00
af821dcd9a Merge pull request #4914 from leigh-pointer/userSettings
User Profile Update: Utilizing User Settings Object
2024-12-18 15:27:34 -05:00
10d3c81520 Merge pull request #4918 from leigh-pointer/ProjectNameTemplates
Implement Dynamic ProjectName Parameter Across Build Process
2024-12-18 15:27:24 -05:00
e3811b453a Merge pull request #4927 from sbwalker/dev
Improvements to add support for script type and data-* attributes. Also added Script and Stylesheet classes to simplify Resource declarations.
2024-12-18 15:16:16 -05:00
ca0fb05baa Improvements to add support for script type and data-* attributes. Also added Script and Stylesheet classes to simplify Resource declarations. 2024-12-18 15:15:54 -05:00
2b4b01bf6e Merge pull request #4926 from sbwalker/dev
always render page-script elements in body
2024-12-18 11:56:18 -05:00
3a1244bddc always render page-script elements in body 2024-12-18 11:56:03 -05:00
b8fd922b19 Merge pull request #4925 from sbwalker/dev
improve reload script to replicate all attributes
2024-12-18 10:32:12 -05:00
03f856025e improve reload script to replicate all attributes 2024-12-18 10:31:59 -05:00
45f04d24c3 Merge pull request #4924 from sbwalker/dev
refactor Static Blazor script processing
2024-12-18 10:09:20 -05:00
2435d610c7 refactor Static Blazor script processing 2024-12-18 10:09:02 -05:00
06572bcd14 Merge pull request #4922 from sbwalker/dev
page-script optimization
2024-12-17 14:57:17 -05:00
3fab79afc0 page-script optimization 2024-12-17 14:57:01 -05:00
2b7dd3fed5 Merge pull request #4921 from sbwalker/dev
render page-script elements at end of head content
2024-12-17 12:31:40 -05:00
d65efed032 render page-script elements at end of head content 2024-12-17 12:31:26 -05:00
c6896ea936 Merge pull request #4920 from sbwalker/dev
fix page-script to support type attribute on inline scripts
2024-12-17 09:13:42 -05:00
b7a41bddec fix page-script to support type attribute on inline scripts 2024-12-17 09:13:26 -05:00
6fd80c3737 Merge pull request #4919 from sbwalker/dev
page-script improvements
2024-12-17 08:34:55 -05:00
0aa690b3b1 page-script improvements 2024-12-17 08:34:39 -05:00
6d5bcfc6ed Implement Dynamic ProjectName Parameter Across Build Process
This pull request introduces a dynamic ProjectName parameter across our build process, enhancing flexibility and reducing hardcoding in our module and theme development workflow. The changes affect the project file, NuSpec file, command scripts.
2024-12-17 10:35:14 +01:00
b60de69fa5 Merge pull request #4917 from sbwalker/dev
improvements to page-script
2024-12-16 12:44:23 -05:00
d991b57d08 improvements to page-script 2024-12-16 12:44:07 -05:00
1133d7fcba User Profile Update: Utilizing User Settings Object
This pull request updates the User Profile implementation to utilize the existing User Settings object on the user object. While the current implementation is functional, this change offers several benefits:
Improved code consistency
Better guidance for developers accessing User Settings
Alignment with the framework's best practices
2024-12-14 11:09:54 +01:00
6fbf0383bb Merge pull request #4911 from leigh-pointer/FixGlow
This is a cosmetic fix to the Oqtane Glow image
2024-12-13 16:46:49 -05:00
0296230219 Merge pull request #4913 from sbwalker/dev
page-script enhancements
2024-12-13 16:46:23 -05:00
dedfbba27a page-script enhancements 2024-12-13 16:46:08 -05:00
dc5441da07 This is a cosmetic fix to the Oqtane Glow image
The glow on the original is cropped at the left by a few pixcels.  This update fixes that and sizes the image to a 600 x 150
2024-12-13 11:45:13 +01:00
585648b7f3 Merge pull request #4907 from leigh-pointer/ProfileMaxLength
FIX for #4906 Profile Field does not allow max length
2024-12-10 09:12:52 -05:00
cd0ee1c26d FIX for #4906 Profile Field does not allow max length
This update applies the maxlength attribute fix to all input and textarea elements. The maxlength attribute will only be added if p.MaxLength is greater than 0, allowing unlimited characters when it's 0 or less.
2024-12-10 09:25:01 +01:00
d7a7be5af4 Merge pull request #4904 from sbwalker/dev
add sync events for user login/logout
2024-12-09 10:55:55 -05:00
13e4267c11 add sync events for user login/logout 2024-12-09 10:55:40 -05:00
15bc47e3e8 Merge pull request #4898 from leigh-pointer/TemplateParam
Implement Dynamic TargetFramework in Debug Scripts #4897
2024-12-09 07:36:00 -05:00
1a4380dcd7 Variable update 2024-12-07 15:02:27 +01:00
5ace34b5cd Add the $TargetFramework macro to the Release Builds
Add the $TargetFramework macro to the Release Builds
2024-12-06 13:37:37 +01:00
f010c0f1fa Implement Dynamic TargetFramework in Debug Scripts #4897
This PR updates our debug scripts (both .cmd and .sh) to dynamically use the current TargetFramework passed from the build process. This change improves flexibility and future-proofs our build process for different .NET versions.
2024-12-05 12:43:38 +01:00
2c721ad5dd Merge pull request #4895 from tvatavuk/enh-4883-control-panel
Add ShowEditMode parameter to ControlPanel to allow hiding the Edit Mode toggle button
2024-12-04 08:25:17 -05:00
8a7c2ce2c2 Merge pull request #4894 from W6HBR/dev
Fix #4885 - Pass userid as int to GetUser for JWT authentication
2024-12-04 08:24:27 -05:00
b2a7b813de Remove redundant IServiceProvider injection 2024-12-04 09:39:02 +01:00
e85cf04b99 Fix #4883: Add ShowEditMode parameter to ControlPanel to allow hiding the Edit Mode toggle button 2024-12-04 09:37:21 +01:00
ab6fa48172 Fix #4885 - Pass userid as int to GetUser for JWT authentication 2024-12-03 10:34:44 -08:00
c81905882f Merge pull request #4881 from sbwalker/dev
improve security of UserRole API
2024-11-27 14:59:41 -05:00
f0d31c1114 improve security of UserRole API 2024-11-27 14:59:25 -05:00
497b255216 Merge pull request #4880 from sbwalker/dev
User Settings should only be accessible to individual users or administrators
2024-11-27 13:16:04 -05:00
d96286d771 User Settings should only be accessible to individual users or administrators 2024-11-27 13:15:43 -05:00
2441647d75 Merge pull request #4879 from sbwalker/dev
update EFCore.NamingConventions to .NET 9 package version
2024-11-27 13:06:53 -05:00
77b780d631 update EFCore.NamingConventions to .NET 9 package version 2024-11-27 13:06:37 -05:00
cdd03bf3d4 Merge pull request #4878 from sbwalker/dev
User Settings should only be accessible to individual users or administrators
2024-11-27 13:04:27 -05:00
e786c35f7d User Settings should only be accessible to individual users or administrators 2024-11-27 13:04:06 -05:00
e83399acb1 Merge pull request #4876 from sbwalker/dev
prevent notifications from being accessed by other users
2024-11-26 14:30:55 -05:00
ffea9e3210 prevent notifications from being accessed by other users 2024-11-26 14:30:41 -05:00
f71a3a1ce3 Merge pull request #4875 from oqtane/revert-4828-TabChange
Revert "Fix for Tabpanel is not updating the UI. #4778"
2024-11-26 13:36:18 -05:00
a5ccc23604 Revert "Fix for Tabpanel is not updating the UI. #4778" 2024-11-26 13:36:05 -05:00
1ed4c8a094 Merge pull request #4874 from oqtane/revert-4871-HTMLTabError
Revert "Rework for Tabstrip regression issue"
2024-11-26 13:35:37 -05:00
4a74549c1b Revert "Rework for Tabstrip regression issue" 2024-11-26 13:35:21 -05:00
a499cfb98f Merge pull request #4871 from leigh-pointer/HTMLTabError
Rework for Tabstrip regression issue
2024-11-26 11:27:52 -05:00
01038c8296 Merge pull request #4873 from sbwalker/dev
include SECURITY.md
2024-11-26 11:25:35 -05:00
7407f79b3d include SECURITY.md 2024-11-26 11:25:23 -05:00
a845dd1976 Rework for Tabstrip regression issue
Fix for Tabpanel is not updating the UI. #4778 #4828
2024-11-26 15:39:51 +01:00
9d7549da70 Reverted TabStrip and Panel 2024-11-26 10:33:18 +01:00
f5cc61384f Merge pull request #4870 from sbwalker/dev
reference Quill CSS theme using BaseUrl so that it works in .NET MAUI as well as web
2024-11-25 14:20:15 -05:00
844778d36a reference Quill CSS theme using BaseUrl so that it works in .NET MAUI as well as web 2024-11-25 14:19:58 -05:00
871b0a274e Merge pull request #4869 from sbwalker/dev
improve message grammar
2024-11-25 13:48:41 -05:00
737740a3ca improve message grammar 2024-11-25 13:48:28 -05:00
ae8d600600 Update README.md 2024-11-25 09:11:12 -05:00
2f1691bfb0 Update README.md 2024-11-25 09:10:20 -05:00
a3d25f91c8 Update README.md 2024-11-25 09:09:52 -05:00
ff84b50817 Update README.md 2024-11-25 09:09:13 -05:00
0be8242284 Merge pull request #4862 from leigh-pointer/SettingsSetTabOnSave
Updated the Module Settings to use the new ActiveTab parameter
2024-11-25 08:39:45 -05:00
e25a6259ea Merge pull request #4861 from zyhfish/task/fix-4841
Fix #4841: force 2FA validation when it's required in site level.
2024-11-25 08:39:26 -05:00
1578f82efb Updated the Module Settings to use the new ActiveTab parameter 2024-11-23 11:42:22 +01:00
Ben
b5f75f0c5e Fix #4841: force 2FA validation when it's required in site level. 2024-11-23 13:04:27 +08:00
601caab3b6 Merge pull request #4860 from sbwalker/dev
fix #4760 - display update confirmation message in Site Settings
2024-11-22 16:34:46 -05:00
6d3092f440 fix #4760 - display update confirmation message in Site Settings 2024-11-22 16:34:35 -05:00
ef27937c7a Merge pull request #4785 from thabaum/refactored-heading-ifelse
Fixes #4784: Refactor TabPanel Heading Assignment Logic
2024-11-22 15:34:51 -05:00
f4a7b79c4f Merge pull request #4828 from leigh-pointer/TabChange
Fix for Tabpanel is not updating the UI. #4778
2024-11-22 15:30:51 -05:00
2531776a48 Merge pull request #4859 from sbwalker/dev
prepare for 6.0.1
2024-11-22 12:29:56 -05:00
ced80419aa prepare for 6.0.1 2024-11-22 12:29:44 -05:00
ad2816f4e8 Merge pull request #4858 from sbwalker/dev
fix #4848 - remove assemblies from /bin which have been moved to /bin/refs in .NET 9
2024-11-22 12:14:04 -05:00
3528b8c674 fix #4848 - remove assemblies from /bin which have been moved to /bin/refs in .NET 9 2024-11-22 12:13:45 -05:00
80c83c626d Merge pull request #4853 from leigh-pointer/PagerAlignment
Fix for #4852 align the Page numbers container
2024-11-22 11:56:12 -05:00
6a355f2aea Merge pull request #4857 from sbwalker/dev
fix #4855 - dropping required column causes issue on SQLite
2024-11-22 11:55:56 -05:00
7d94e4a53a fix #4855 - dropping required column causes issue on SQLite 2024-11-22 11:55:43 -05:00
823c04742e Merge pull request #4854 from sbwalker/dev
resolve .NET version issue in nuspec files
2024-11-21 10:54:06 -05:00
043fb1abd1 resolve .NET version issue in nuspec files 2024-11-21 10:53:52 -05:00
f01e85b690 Fix for #4852 align the Page numbers container 2024-11-21 16:05:32 +01:00
7eb1298847 Merge pull request #4847 from sbwalker/dev
do not include Oqtane.Server.staticwebassets.endpoints.json in release packages
2024-11-19 13:50:05 -05:00
a5480c9a96 do not include Oqtane.Server.staticwebassets.endpoints.json in release packages 2024-11-19 13:49:45 -05:00
f948600e86 Merge pull request #4845 from sbwalker/dev
fix 2 factor authentication email
2024-11-18 15:04:01 -05:00
420182b9bf fix 2 factor authentication email 2024-11-18 15:03:48 -05:00
8c430ce1a6 Merge pull request #4837 from Trifoia/4803-Add-a-CONTRIBUTING.md
4803 add a contributing.md
2024-11-15 15:02:28 -05:00
d3717dbe19 Remove DefaultDBType value and InstallationId from appsettings.json
The commit removes the value for DefaultDBType and InstallationId from the appsettings.json file.
2024-11-15 10:31:49 -08:00
caa83d769f Create CONTRIBUTING.md 2024-11-15 10:27:53 -08:00
365f87828f Merge remote-tracking branch 'oqtane/dev' into dev 2024-11-15 09:52:50 -08:00
f780887866 Update README.md 2024-11-14 15:46:26 -05:00
43627d4bb8 Merge pull request #4832 from oqtane/master
6.0.0 release
2024-11-14 15:40:38 -05:00
5d7b276cd1 Merge pull request #4831 from oqtane/dev
6.0.0 release
2024-11-14 15:40:17 -05:00
23597eb997 Merge pull request #4827 from leigh-pointer/Search-4824
Fix: for Disabling search does not hide search icon #4824
2024-11-14 07:54:52 -05:00
b6948367f8 Merge pull request #4826 from leigh-pointer/swash
Swashbuckle.AspNetCore updated to 7.0
2024-11-14 07:52:49 -05:00
db6dd5abee Fix for TabPanel is not updating the UI. #4778
Modified that TabStrip and TabPane, now when the ActiveTab is changed the TabPanel is selected
2024-11-14 10:35:15 +01:00
702eb9e466 Revert "Fix for Page Management tab panel is not updating the UI. #4778"
This reverts commit 3c99006226.
2024-11-14 10:33:10 +01:00
3c99006226 Fix for Page Management tab panel is not updating the UI. #4778 2024-11-14 10:31:53 +01:00
aaaf5683a5 Fix: "Search_Enabled" was being saved as Private so unauthorized users can not access this value. 2024-11-14 09:50:05 +01:00
92aa2236c0 Swashbuckle.AspNetCore updated to 7.0 2024-11-14 09:16:55 +01:00
d2592f72d6 Merge pull request #4825 from sbwalker/dev
modifications to get .NET MAUI working on .NET 9 official release
2024-11-13 20:37:24 -05:00
27120d6cc9 modifications to get .NET MAUI working on .NET 9 official release 2024-11-13 20:37:10 -05:00
a2669d35c3 Merge pull request #4823 from sbwalker/dev
exclude wwwroot/_content from official release build
2024-11-13 14:14:47 -05:00
574164081b exclude wwwroot/_content from official release build 2024-11-13 14:14:35 -05:00
00c2f8dcd8 Merge pull request #4822 from sbwalker/dev
adjust gitignore to exclude wwwroot/_content subfolders
2024-11-13 13:52:08 -05:00
0202bf60e5 adjust gitignore to exclude wwwroot/_content subfolders 2024-11-13 13:51:54 -05:00
16436a171b Merge pull request #4821 from zyhfish/task/fix-build-error
add reference to Microsoft.EntityFrameworkCore package.
2024-11-13 10:10:19 -05:00
Ben
4cf4e0eabd add reference to Microsoft.EntityFrameworkCore package. 2024-11-13 23:06:43 +08:00
5edb98dfb4 Merge pull request #4818 from sbwalker/dev
update PostgreSQL provider to official .NET 9 release
2024-11-12 15:50:27 -05:00
899bf22e15 update PostgreSQL provider to official .NET 9 release 2024-11-12 15:50:13 -05:00
9a33167a6c Merge pull request #4817 from sbwalker/dev
Update to official .NET 9 release
2024-11-12 15:43:42 -05:00
2dc068aa21 upgrade to official .NET 9 release 2024-11-12 15:42:57 -05:00
0f2aa4d2e1 update to official .NET 9 release 2024-11-12 15:40:54 -05:00
a699f5c7bc Merge pull request #4815 from mdmontesinos/dev
Fix: Accesibility issue for Search Button
2024-11-12 15:08:23 -05:00
ab807de3c0 Fix: Accesibility issue for Search Button
The search button in Search component does not have the aria-label.
2024-11-12 17:15:56 +01:00
68514bcb36 Merge pull request #4813 from sbwalker/dev
use HttpClient rather than IHttpClientFactory as IHttpClientFactory does not pass cookies in .NET MAUI
2024-11-11 08:15:14 -05:00
422bf8da59 use HttpClient rather than IHttpClientFactory as IHttpClientFactory does not pass cookies in .NET MAUI 2024-11-11 08:14:53 -05:00
de92dc93dd Merge pull request #4811 from sbwalker/dev
resolved issue when setting initial culture cookie
2024-11-08 15:41:36 -05:00
5a91b143b6 resolved issue when setting initial culture cookie 2024-11-08 15:41:24 -05:00
c745e85706 Merge pull request #4810 from sbwalker/dev
make indexing of Files opt-in rather than opt-out
2024-11-08 15:16:45 -05:00
0f698e0c50 make indexing of Files opt-in rather than opt-out 2024-11-08 15:16:32 -05:00
cd16d77bf0 Merge pull request #4809 from sbwalker/dev
Added defensive logic to File Indexer for scenarios where file does not exist on disk. Added ability to reset the search index prior to reindexing.
2024-11-07 17:05:48 -05:00
fdbdd0ef4c Added defensive logic to File Indexer for scenarios where file does not exist on disk. Added ability to reset the search index prior to reindexing. 2024-11-07 17:05:28 -05:00
71485f4a82 Merge pull request #4808 from sbwalker/dev
fix compilation warning
2024-11-07 15:45:47 -05:00
013056a6e5 fix compilation warning 2024-11-07 15:45:34 -05:00
263498fbdc Merge pull request #4807 from sbwalker/dev
remove icrosoft.AspNetCore.Localization from .NET MAUI project
2024-11-07 12:38:31 -05:00
f46ac2c007 remove icrosoft.AspNetCore.Localization from .NET MAUI project 2024-11-07 12:38:16 -05:00
c23841b6db Merge pull request #4806 from sbwalker/dev
default Description to Module Name if not specified in Module Creator
2024-11-07 11:53:15 -05:00
18b1b5fca5 default Description to Module Name if not specified in Module Creator 2024-11-07 11:52:53 -05:00
6707ac2697 Merge pull request #4802 from sbwalker/dev
remove custom JavaScript reconnection script for SignalR
2024-11-05 11:30:32 -05:00
d85a2fc8ce remove custom JavaScript reconnection script for SignalR 2024-11-05 11:30:18 -05:00
a8997e8f17 Merge pull request #4800 from sbwalker/dev
fix #4795 - ensure deterministic ordering of file parts when merging files after upload (credit @HQuast)
2024-11-04 15:29:16 -05:00
c8a22d9537 fix #4795 - ensure deterministic ordering of file parts when merging files after upload (credit @HQuast) 2024-11-04 15:28:59 -05:00
910669f786 Merge pull request #4793 from thabaum/patch-12
Fixes #4792: Update Oqtane.Server.csproj Package Dependencies
2024-11-01 15:40:14 -04:00
89312c6796 Update Oqtane.Server.csproj Package Dependencies 2024-10-30 18:43:42 -07:00
a6aa96fdb0 Merge pull request #4786 from sbwalker/dev
fix #4770 - set a default value for PrincipalSchema to ensure backward compatibility
2024-10-24 17:08:42 -04:00
7deb0b06af fix #4770 - set a default value for PrincipalSchema to ensure backward compatibility 2024-10-24 17:08:25 -04:00
784f3771b3 Remove Blank Page Specific Error Message 2024-10-24 13:22:08 -07:00
80316824f7 Remove Blank Page Specific Error Message 2024-10-24 13:21:40 -07:00
cbaebb7b7c Remove Blank Page Specific Error Message 2024-10-24 13:21:15 -07:00
97d3764b6e Remove Blank Page Specific Error Message 2024-10-24 13:20:52 -07:00
002f8117cb Merge pull request #4783 from sbwalker/dev
get language using CookieRequestCultureProvider
2024-10-24 15:48:28 -04:00
0dfdb12431 get language using CookieRequestCultureProvider 2024-10-24 15:48:14 -04:00
d77e898929 Refactor TabPanel Heading Assignment Logic
- Simplified the logic for setting the Heading property in the TabPanel component.
- Replaced the if-else statement with a ternary operator for improved readability and maintainability.
- Ensured that the functionality remains unchanged and verified correct assignment of headings.
2024-10-24 12:43:20 -07:00
4c937be884 Clarify Page Name Required Message 2024-10-24 12:38:51 -07:00
c25cce4398 Clarify Page Name Required Message 2024-10-24 12:38:16 -07:00
58c422285a Adds Message.Required.PageName 2024-10-24 12:28:17 -07:00
f2f22f35e8 Adds Message.Required.PageName 2024-10-24 12:26:36 -07:00
15867a7807 Adds await ScrollToPageTop(); to error messages + Page Name Error Message 2024-10-24 12:23:29 -07:00
22e3161a9b Merge pull request #4780 from sbwalker/dev
remove Microsoft.AspNetCore.Localization from default module template
2024-10-24 15:20:20 -04:00
b0c8203b24 remove Microsoft.AspNetCore.Localization from default module template 2024-10-24 15:20:07 -04:00
e2c404d8bb Adds await ScrollToPageTop(); to error messages + Page Name Error Message
- Adds await ScrollToPageTop(); to error messages
- Adds Blank Page Name Error Message
2024-10-24 12:18:30 -07:00
5ee1731c92 Merge pull request #4777 from thabaum/update-html-agility-pack-1.11.69
Fixes #4776: Updates HtmlAgilityPack to 1.11.69
2024-10-24 15:12:17 -04:00
06e8d3b660 Merge pull request #4779 from sbwalker/dev
remove Microsoft.AspNetCore.Http
2024-10-24 15:11:07 -04:00
f09709aedb remove Microsoft.AspNetCore.Http 2024-10-24 15:10:52 -04:00
598d5decac Update HtmlAgilityPack to 1.11.69 2024-10-24 10:18:24 -07:00
7832a6053e Merge pull request #4775 from sbwalker/dev
remove some dependencies on Microsoft.AspNetCore.Http
2024-10-24 13:16:21 -04:00
588748230e remove some dependencies on Microsoft.AspNetCore.Http 2024-10-24 13:16:08 -04:00
8668165c72 Merge pull request #4774 from sbwalker/dev
remove Microsoft.AspNetCore.Localization
2024-10-24 12:25:04 -04:00
a967332f89 remove Microsoft.AspNetCore.Localization 2024-10-24 12:24:46 -04:00
6719d242bd Merge pull request #4756 from zyhfish/task/fix-4752
Fix #4752: validate the username and email.
2024-10-24 09:53:24 -04:00
Ben
3565185808 update the error message. 2024-10-24 20:13:43 +08:00
Ben
ce51262197 update the code to use simple validation. 2024-10-24 20:04:18 +08:00
992a786c2b Merge pull request #4763 from thabaum/ScrollTo-interop-modal
Fixes #4762: Adds logic ScrollTo interop.js method detecting if method is executed inside a modal
2024-10-22 16:28:45 -04:00
038df95aa0 Merge pull request #4759 from thabaum/scroll-to-top-user-settings
Scroll to top after saving settings - Fixes #4758
2024-10-22 16:28:33 -04:00
4ebd660de2 Merge pull request #4766 from sbwalker/dev
resolve compiler warning in .NET MAUI
2024-10-21 15:42:08 -04:00
a9ea41a488 resolve compiler warning in .NET MAUI 2024-10-21 15:41:57 -04:00
196594b490 Merge pull request #4765 from sbwalker/dev
update MAUI project to .NET 9 RC2
2024-10-21 15:34:34 -04:00
1516d5af10 update MAUI project to .NET 9 RC2 2024-10-21 15:34:22 -04:00
ffcd1595a9 Merge pull request #4764 from sbwalker/dev
update Updater to .NET 9
2024-10-21 15:25:27 -04:00
42e5c6e111 update Updater to .NET 9 2024-10-21 15:25:14 -04:00
8a9651dc50 Adds logic for .modal class ScrollTo function 2024-10-21 12:22:01 -07:00
4be2f4f2d9 Update README.md 2024-10-21 15:21:29 -04:00
369bf7a235 Adds logic for .modal class ScrollTo function
fixes #4762
2024-10-21 12:20:24 -07:00
136545b404 Merge pull request #4761 from sbwalker/dev
changes to migrate Oqtane to .NET 9 and version 6.0.0
2024-10-21 14:51:38 -04:00
73ea17ae0f changes to migrate Oqtane to .NET 9 and version 6.0.0 2024-10-21 14:51:20 -04:00
23010acef4 Scroll to top after saving settings - Fixes #4758 2024-10-21 10:36:12 -07:00
Ben
4f74962ce2 Fix #4752: validate the username and email. 2024-10-21 23:11:57 +08:00
7f978c7845 Update README.md 2024-10-17 13:52:21 -04:00
731fd46ea2 Merge pull request #4747 from oqtane/master
5.2.4 release
2024-10-17 13:46:40 -04:00
859759d691 Merge pull request #4746 from oqtane/dev
5.2.4 release
2024-10-17 13:46:22 -04:00
5e9567158f Merge pull request #4744 from sbwalker/dev
add additional external login providers
2024-10-16 08:35:14 -04:00
51d244f3aa add additional external login providers 2024-10-16 08:35:01 -04:00
4c5a07edd5 Merge pull request #4743 from sbwalker/dev
add missing localization keys
2024-10-16 07:52:49 -04:00
8113a754a1 add missing localization keys 2024-10-16 07:52:36 -04:00
3cd40c6195 Merge pull request #4742 from sbwalker/dev
fix sorting of Site.Languages property
2024-10-15 16:46:16 -04:00
56cfb2ce06 fix sorting of Site.Languages property 2024-10-15 16:46:05 -04:00
72087823ac Merge pull request #4741 from sbwalker/dev
fix #4733 - remove Name column from Language table and populate value dynamically
2024-10-15 16:44:44 -04:00
bcf7866fe2 fix #4733 - remove Name column from Language table and populate value dynamically 2024-10-15 15:58:17 -04:00
b64772e484 Merge pull request #4739 from thabaum/add-discord-community
Fixes #4738 - Add Discord community button to README.md
2024-10-15 15:26:32 -04:00
088d665942 Update Discord Community Link For Consistency 2024-10-15 10:27:59 -07:00
7d6c10befb Add Discord community button to README.md
- Added a button for joining the Oqtane Discord server
- Included a brief description encouraging community engagement
2024-10-15 10:17:01 -07:00
f88e3d04b8 Merge pull request #4736 from thabaum/mysql.data-9.1.0
Fixes #4735 - Update MySQL.Data to 9.1.0
2024-10-15 12:06:50 -04:00
f57676a22b Update MySQL.Data to 9.1.0 2024-10-15 08:15:52 -07:00
8618cb62e4 Merge pull request #4732 from sbwalker/dev
fix #4711 - full page refresh required to affect language changes
2024-10-15 08:32:08 -04:00
c31c88ed1f fix #4711 - full page refresh required to affect language changes 2024-10-15 08:31:54 -04:00
6022acd21f Merge pull request #4731 from sbwalker/dev
fix #4716 - sort recycle bin items by DeletedOn date descending
2024-10-15 07:56:07 -04:00
b3071b9272 fix #4716 - sort recycle bin items by DeletedOn date descending 2024-10-15 07:55:54 -04:00
ec6a6d6653 Merge pull request #4729 from sbwalker/dev
localize names of languages based on user's UI culture
2024-10-14 17:18:09 -04:00
52f552b4de localize names of languages based on user's UI culture 2024-10-14 17:17:54 -04:00
2643d3396b Merge pull request #4728 from sbwalker/dev
set HttpOnly to false for Localization cookie in static rendering
2024-10-14 16:49:27 -04:00
62d59a09cf set HttpOnly to false for Localization cookie in static rendering 2024-10-14 16:49:14 -04:00
a68ff8a4f0 Merge pull request #4727 from sbwalker/dev
When displaying Database Type use SQL Server rather than LocalDB to avoid confusion
2024-10-14 16:21:25 -04:00
93d4bfcd7a When displaying Database Type use SQL Server rather than LocalDB to avoid confusion 2024-10-14 16:21:03 -04:00
5fb80c1a7b Merge pull request #4726 from sbwalker/dev
fix #4722 - support PrincipalSchema when creating foreign keys (credit @Hypnodude)
2024-10-14 15:36:54 -04:00
04b38444ce fix #4722 - support PrincipalSchema when creating foreign keys (credit @Hypnodude) 2024-10-14 15:36:32 -04:00
ca3df02002 Merge pull request #4723 from mdmontesinos/dev
Image Resizing and Format via QueryString in Files Page (enhances #4692)
2024-10-14 15:20:37 -04:00
d952c33fab Merge pull request #4725 from sbwalker/dev
fix #4714 as well as breaking change in #4712
2024-10-14 15:05:59 -04:00
93bc1cd5af fix #4714 as well as breaking change in #4712 2024-10-14 15:05:46 -04:00
0e5b370ee8 Merge pull request #4712 from thabaum/language-switcher-cookie
Fix #4710 - Adds language switcher component cookie set options for secure, httpOnly, sameSite + interop.cs/interop.js methods samesite and secure options
2024-10-14 14:41:18 -04:00
f4fd4e28c9 Merge pull request #4724 from sbwalker/dev
update MAUI project to .NET 8.0.10
2024-10-14 13:12:09 -04:00
ec8433eb45 update MAUI project to .NET 8.0.10 2024-10-14 13:11:55 -04:00
0d4a40e9bb Merge pull request #4715 from leigh-pointer/CompUpdate-8.0.10
Updated Microsoft Components for 8.0.8 to 8.0.10
2024-10-14 13:04:54 -04:00
c3668f4179 Merge pull request #4708 from thabaum/patch-10
fix typo in startup.cs
2024-10-14 12:48:38 -04:00
3adb7ecb1c Enhances image manipulation with format (webp encoder, defaults to png)
- computes etag with all manipulation parameters
2024-10-13 17:20:18 +02:00
aa5b84a214 Implements Image Manipulation in Files Page via QueryString
- Extracts the image creation into a service
- Refactors Files Page GET action for better readability and cyclomatic complexity
2024-10-13 12:38:43 +02:00
3d83fccbf1 remove appsettings 2024-10-09 15:07:57 +02:00
4c4255be6b Updated Template files with new component version 8.0.10 2024-10-09 14:42:07 +02:00
ed6054b082 Updated Microsoft Components for 8.0.8 to 8.0.10
Tested on upgrade and new Oqtane instance creation.
2024-10-09 14:35:27 +02:00
f60f7a4dc1 Remove httpOnly setting from setCookie 2024-10-05 14:17:20 -07:00
998dc95cb2 Removed extra attribute for interop.SetCookie 2024-10-05 14:08:16 -07:00
12f5d7b846 Remove extra attribute for SetCookie 2024-10-05 14:06:35 -07:00
906ae0a43e Remove extra attribute for SetCookie 2024-10-05 14:06:18 -07:00
485b774876 Remove httpOnly cooking attribute from SetCookie 2024-10-05 14:03:20 -07:00
3121cf5b75 Remove unnecessary httpOnly setCookie option 2024-10-05 14:00:18 -07:00
ce7570dae2 Remove Unnecessary httpOnly setting setCookie Option 2024-10-05 13:58:55 -07:00
b5ea0dfbc7 Update Cleanup "setCookie" function notes options: secure, httpOnly, Samesite 2024-10-05 13:57:36 -07:00
dd0f8f4772 Update SetCookie function to include secure, httpOnly and sameSite 2024-10-05 13:55:35 -07:00
9d0ab34274 Update 'SetCookie" option settings "secure, httpOnly, sameSite" 2024-10-05 13:28:33 -07:00
e6038be6f7 Update SetCookie Option Settings Secure, HttpOnly, SameSite 2024-10-05 13:27:14 -07:00
bd2153a0ed Update cookie options to set SameSite, HttpOnly, Secure settings 2024-10-05 13:23:09 -07:00
e526deac20 Update Cookie Settings Secure, httpOnly, sameSite 2024-10-05 13:19:52 -07:00
b65f165dcf Update adds SameSite, Secure and httpOnly SetCookie Settings 2024-10-05 13:17:31 -07:00
f9fbe5adc2 Update NavigateTo() to use "true" instead of "forceLoad: true" 2024-10-05 12:11:52 -07:00
d1e73571a1 fix typo 2024-10-05 11:12:01 -07:00
1047058676 Merge pull request #4705 from maurocavallin/dev
Hard deletion of page more robust use of entity framework contexts
2024-10-04 17:14:00 -04:00
29a1e77da8 Hard deletion of page more robust use of contexts (fixes issue: presence of stale records of deleted page on db). 2024-10-04 22:46:40 +02:00
290547e482 Merge pull request #4702 from sbwalker/dev
add defensive logic if ModuleState is null  in ModuleMessage
2024-10-02 16:53:46 -04:00
3df45ca20f add defensive logic if ModuleState is null in ModuleMessage 2024-10-02 16:53:36 -04:00
cc06258484 Merge pull request #4701 from sbwalker/dev
updated version to 5.2.4
2024-10-02 16:39:45 -04:00
2c262d0655 updated version to 5.2.4 2024-10-02 16:39:31 -04:00
1875e1e158 Merge pull request #4696 from sbwalker/dev
fix #4695 - null reference exception deleting a setting which does not exist
2024-10-02 08:30:58 -04:00
1c95967b31 fix #4695 - null reference exception deleting a setting which does not exist 2024-10-02 08:30:34 -04:00
2ae98929de Merge pull request #4689 from thabaum/Update-Dependencies-and-Prepare-Release-5.2.4
Fixes #4688 - Update dependencies and prepare release 5.2.4
2024-10-01 11:21:35 -04:00
6a1dd38cbb Merge pull request #4691 from zyhfish/task/fix-4690
Fix #4690: prevent invalid parsing.
2024-10-01 11:20:01 -04:00
Ben
c458a77d27 Fix #4690: prevent invalid parsing. 2024-10-01 22:13:32 +08:00
352c23f389 Prepare Release 5.2.4 2024-10-01 06:15:46 -07:00
6c5a1dc2e1 Prepare Release 5.2.4 2024-10-01 06:14:57 -07:00
004ff1e91d Update Dependencies and Prepare Release 5.2.4 2024-10-01 06:14:07 -07:00
f7de4c567b Prepare Release 5.2.4 2024-10-01 06:12:07 -07:00
b2ad1010ac Prepare Release 5.2.4 2024-10-01 06:11:36 -07:00
660e164ff8 Prepare Release 5.2.4 2024-10-01 06:11:04 -07:00
eb6dc80b50 Prepare Release 5.2.4 - removed whitespace 2024-10-01 06:10:21 -07:00
a9882cc96a Update Oqtane.Shared.nuspec 2024-10-01 06:09:35 -07:00
fb5a2ce178 Prepare Release 5.2.4 2024-10-01 06:09:12 -07:00
d441b31dc7 Prepare Release 5.2.4 2024-10-01 06:08:46 -07:00
d4239fe7e0 Prepare Release 5.2.4 2024-10-01 06:07:55 -07:00
6a98999105 Prepare Release 5.2.4 2024-10-01 06:06:03 -07:00
5caa1fe7d4 Prepare Release 5.2.4 2024-10-01 06:05:07 -07:00
ef33bdb65e Prepare Release 5.2.4 2024-10-01 06:04:11 -07:00
14a463382b Prepare Release 5.2.4 2024-10-01 06:03:20 -07:00
1ad79874c8 Update Dependencies and Prepare Release 5.2.4 2024-10-01 06:02:40 -07:00
1a61a58d28 Prepare Release 5.2.4 2024-10-01 06:00:20 -07:00
3e3c973679 Prepare Release 5.2.4 2024-10-01 05:59:47 -07:00
9dede84d20 Update Dependencies and Prepare Release 5.2.4 2024-10-01 05:57:17 -07:00
e78b11cf62 Merge pull request #4687 from sbwalker/dev
add some clarity to the database fields help text
2024-09-30 13:32:08 -04:00
341ca5a330 add some clarity to the database fields help text 2024-09-30 13:31:57 -04:00
e5d8c02def Merge pull request #4686 from sbwalker/dev
add validation of recipient email address to Notification job
2024-09-30 11:27:59 -04:00
947aa08c42 add validation of recipient email address to Notification job 2024-09-30 11:27:47 -04:00
9a04d436bd Merge pull request #4683 from sbwalker/dev
simplify configuration of external login providers
2024-09-27 16:21:23 -04:00
be0754f568 simplify configuration of external login providers 2024-09-27 16:21:06 -04:00
93c4bbc0d1 Merge pull request #4682 from sbwalker/dev
fix #4654 - show progress indicator during download
2024-09-27 12:01:07 -04:00
b98535810b fix #4654 - show progress indicator during download 2024-09-27 12:00:49 -04:00
393cf8da1f Merge pull request #4681 from sbwalker/dev
add disclaimer to System Update feature
2024-09-27 09:00:15 -04:00
ea2846973a add disclaimer to System Update feature 2024-09-27 09:00:04 -04:00
3398c1cbfe Merge pull request #4678 from sbwalker/dev
remove unnecessary using statement
2024-09-26 15:54:35 -04:00
39c79ea68e remove unnecessary using statement 2024-09-26 15:54:22 -04:00
66900f4a32 Merge pull request #4677 from sbwalker/dev
sign out the principal when it is rejected due to security stamp changes
2024-09-26 15:53:55 -04:00
df71dd14f7 sign out the principal when it is rejected due to security stamp changes 2024-09-26 15:53:14 -04:00
8113ca3069 Merge pull request #4676 from sbwalker/dev
fix #4657 - Cannot add new site to existing installation using separate database On IIS
2024-09-26 14:07:11 -04:00
d468e675c2 fix #4657 - Cannot add new site to existing installation using separate database On IIS 2024-09-26 14:06:51 -04:00
1e84cedf82 Merge pull request #4674 from sbwalker/dev
fix localization spelling mistake
2024-09-26 13:46:41 -04:00
7f4087e3de fix localization spelling mistake 2024-09-26 13:46:29 -04:00
facd3c8956 Merge pull request #4673 from sbwalker/dev
fix remote login issue which could occut if multiple users have the same email address
2024-09-26 13:38:30 -04:00
3e50deecb7 fix remote login issue which could occut if multiple users have the same email address 2024-09-26 13:37:39 -04:00
628c504f84 Merge pull request #4672 from sbwalker/dev
fix #4667 - installation issues when running on IIS
2024-09-26 13:33:31 -04:00
e1ada78c1f fix #4667 - installation issues when running on IIS 2024-09-26 13:33:16 -04:00
09fa1e365c Merge pull request #4670 from thabaum/remove-unnecessary-usings
Fixes #4669 - Remove unnecessary usings in IMultiDatabase.cs
2024-09-26 13:32:33 -04:00
28b6b03d06 Remove unnecessary usings 2024-09-25 15:13:06 -07:00
a4395b62ff Merge pull request #4668 from sbwalker/dev
fix #4666 - scroll position in enhanced navigation
2024-09-25 17:06:03 -04:00
4511acf273 fix #4666 - scroll position in enhanced navigation 2024-09-25 17:05:53 -04:00
fde53a2d83 Update README.md 2024-09-23 16:33:41 -04:00
f6cd04fdb8 Merge pull request #4662 from oqtane/master
Merge pull request #4661 from oqtane/dev
2024-09-23 16:26:39 -04:00
a5eede6c7a Merge pull request #4661 from oqtane/dev
5.2.3 release
2024-09-23 16:26:24 -04:00
2e83817c83 Merge pull request #4660 from sbwalker/dev
prepare for 5.2.3
2024-09-23 16:18:10 -04:00
82aea40ae4 prepare for 5.2.3 2024-09-23 16:17:55 -04:00
1b289eae24 Merge pull request #4659 from sbwalker/dev
use RoleName rather than RoleId for consistency
2024-09-23 16:08:02 -04:00
81420b2c88 use RoleName rather than RoleId for consistency 2024-09-23 16:07:49 -04:00
775731b745 Merge pull request #4657 from thabaum/patch-9
FIX #4655 - autocomplete="off" for SMTP Username/Password
2024-09-23 15:44:06 -04:00
489e7d4a67 Merge pull request #4658 from sbwalker/dev
fix RoleId missing from Permission Clone method
2024-09-23 15:38:51 -04:00
b6508764d8 fix RoleId missing from Permission Clone method 2024-09-23 15:38:35 -04:00
6dedd87305 autocomplete="off" for SMTP Username/Password 2024-09-23 12:19:49 -07:00
89fa29b310 Merge pull request #4653 from sbwalker/dev
improve scroll position script
2024-09-23 09:55:59 -04:00
be5df9c22a improve scroll position script 2024-09-23 09:55:44 -04:00
db17739716 Update README.md 2024-09-23 07:48:14 -04:00
1439362a5e Merge pull request #4651 from oqtane/master
5.2.2 Release
2024-09-23 07:39:21 -04:00
65c1b04772 Merge pull request #4650 from oqtane/dev
5.2.2 Release
2024-09-23 07:39:02 -04:00
83d30ebdc4 Merge pull request #4645 from sbwalker/dev
fix #4638 - add Logout Everywhere option to User Profile
2024-09-20 15:18:38 -04:00
b7928a5255 fix #4638 - add Logout Everywhere option to User Profile 2024-09-20 15:18:25 -04:00
a83ff9253d Merge pull request #4644 from sbwalker/dev
fix code formatting
2024-09-20 12:54:44 -04:00
8cdcdaf6d9 fix code formatting 2024-09-20 12:54:34 -04:00
d208edf153 Merge pull request #4643 from sbwalker/dev
fix #4641 - highlight default site theme in list of themes in page management
2024-09-20 12:28:06 -04:00
8ada4765b1 fix #4641 - highlight default site theme in list of themes in page management 2024-09-20 12:27:49 -04:00
9d3a808c24 Merge pull request #4642 from sbwalker/dev
fix #4826 - improve url handling
2024-09-20 12:09:16 -04:00
828eb80266 fix #4826 - improve url handling 2024-09-20 12:09:04 -04:00
0639f6c1d1 Merge pull request #4640 from sbwalker/dev
fix #4628 - preserve Url for all ModuleActions
2024-09-19 14:09:55 -04:00
49b971280f fix #4628 - preserve Url for all ModuleActions 2024-09-19 14:09:45 -04:00
3682a1010d Merge pull request #4637 from sbwalker/dev
synchronize interop.js with .NET MAUI
2024-09-19 12:14:36 -04:00
f8b58866dc synchronize interop.js with .NET MAUI 2024-09-19 12:14:24 -04:00
5d8d815d84 Merge pull request #4636 from zyhfish/task/4610-loadjs-issue
Fix #4610: wait for js loaded.
2024-09-19 12:10:55 -04:00
Ben
67743c7597 Fix #4610: wait for js loaded. 2024-09-19 23:34:59 +08:00
b33cc90447 Merge pull request #4635 from sbwalker/dev
use deep cloning to not muttate cache
2024-09-19 09:41:24 -04:00
78177f7890 use deep cloning to not muttate cache 2024-09-19 09:41:11 -04:00
3c417bfa99 Merge pull request #4633 from sbwalker/dev
fix trimming of site, page, and module settings
2024-09-18 18:32:04 -04:00
f2c8d80ff8 fix trimming of site, page, and module settings 2024-09-18 18:31:40 -04:00
8c1eb1e19f Merge pull request #4632 from sbwalker/dev
fix #4257 - load settings resources
2024-09-18 15:22:58 -04:00
eb7188e81b fix #4257 - load settings resources 2024-09-18 15:22:44 -04:00
f352fc5c67 Merge pull request #4631 from sbwalker/dev
on .NET MAUI provide message to user if Security Token has not been configured
2024-09-18 11:28:30 -04:00
ac313722f9 on .NET MAUI provide message to user if Security Token has not been configured 2024-09-18 11:28:11 -04:00
64c7f1962c Merge pull request #4630 from zyhfish/task/sync-maui-resources
sync changes to maui resources.
2024-09-18 10:46:34 -04:00
Ben
c83f994b21 sync changes to maui resources. 2024-09-18 22:44:14 +08:00
10f38a72f7 Merge pull request #4629 from sbwalker/dev
fix #4628 - preserve ReturnUrl when navigating to Settings component
2024-09-18 09:43:11 -04:00
da35434f58 fix #4628 - preserve ReturnUrl when navigating to Settings component 2024-09-18 09:42:56 -04:00
c79409e094 Merge pull request #4627 from sbwalker/dev
use FileLogger as fallback in LogManager when site cannot be determined
2024-09-18 07:38:20 -04:00
355ce00968 use FileLogger as fallback in LogManager when site cannot be determined 2024-09-18 07:37:52 -04:00
56c832f3ba Merge pull request #4626 from sbwalker/dev
fix logic to force authenticated user to provide email address so it works in static rendering
2024-09-17 17:29:30 -04:00
40abb2720e fix logic to force authenticated user to provide email address so it works in static rendering 2024-09-17 17:29:11 -04:00
9b6051afee Merge pull request #4625 from sbwalker/dev
update nuget.exe to latest and fix nuspec specification of readme
2024-09-17 15:19:57 -04:00
dcf6f26792 update nuget.exe to latest and fix nuspec specification of readme 2024-09-17 15:19:40 -04:00
85734c1146 Merge pull request #4624 from sbwalker/dev
remove unecessary using
2024-09-17 11:54:33 -04:00
c3fb8fcb6e remove unecessary using 2024-09-17 11:54:21 -04:00
013bbc1638 Merge pull request #4623 from sbwalker/dev
fix external login
2024-09-17 11:53:47 -04:00
b0669a3b60 fix external login 2024-09-17 11:53:34 -04:00
6f5da1ce7c Merge pull request #4622 from sbwalker/dev
allow external login to support SecurityStamp
2024-09-17 09:28:47 -04:00
3351732a2f allow external login to support SecurityStamp 2024-09-17 09:28:35 -04:00
d4d4034ecd Merge pull request #4621 from sbwalker/dev
remove unecessary usings
2024-09-17 09:07:57 -04:00
b45e2742c3 remove unecessary usings 2024-09-17 09:07:45 -04:00
42eba290e4 Merge pull request #4620 from sbwalker/dev
allow JwtMiddleware to support SecurityStamp
2024-09-17 09:07:14 -04:00
32d1e08b57 allow JwtMiddleware to support SecurityStamp 2024-09-17 09:06:49 -04:00
7b7d19da7c Merge pull request #4619 from sbwalker/dev
fix commented logic
2024-09-17 08:55:09 -04:00
f78e400918 fix commented logic 2024-09-17 08:54:52 -04:00
be4e9bf7e9 Merge pull request #4618 from sbwalker/dev
fix #4580 - add logout everywhere support using SecurityStamp
2024-09-17 08:45:41 -04:00
48f2079f88 fix #4580 - add logout everywhere support using SecurityStamp 2024-09-17 08:45:27 -04:00
36ad1ceef2 Merge pull request #4617 from sbwalker/dev
fix #4607 - site level scripts added twice in static rendering
2024-09-16 13:22:10 -04:00
1f2e2148d5 fix #4607 - scripts added twice 2024-09-16 13:21:43 -04:00
76f3d345f9 Merge pull request #4616 from sbwalker/dev
include SecurityStamp in User object
2024-09-16 13:03:32 -04:00
f2c854b53a include SecurityStamp in User object 2024-09-16 13:03:21 -04:00
e2d336d90b Merge pull request #4613 from sbwalker/dev
further modifications for #4604 - support for site name in logo component
2024-09-15 09:30:24 -04:00
c74065ff26 further modifications for #4604 - support for site name in logo component 2024-09-15 09:30:04 -04:00
b6d97dc5d5 Merge pull request #4612 from sbwalker/dev
fix #4606 - allow logo to show site name as fallback (credit @JanOlsmar)
2024-09-13 16:13:14 -04:00
1c1c26948a fix #4606 - allow logo to show site name as fallback (credit @JanOlsmar) 2024-09-13 16:13:01 -04:00
d954e3ffb7 Merge pull request #4608 from hishamco/toggle-edit-mode
Fix issue in toggle edit mode
2024-09-13 15:54:37 -04:00
d9759a95eb Merge pull request #4611 from sbwalker/dev
fix #4607 - Site HeadContent scripts being added twice
2024-09-13 15:19:31 -04:00
d196402dd0 fix #4607 - Site HeadContent scripts being added twice 2024-09-13 15:18:12 -04:00
0f2b5531de Merge pull request #4601 from hishamco/module-settings
Add null operator in ModuleState.ModuleDefinition
2024-09-13 14:13:40 -04:00
26e4398905 Address feedback 2024-09-13 21:12:18 +03:00
06995f22fe Merge pull request #4609 from sbwalker/dev
improve support for external login roles
2024-09-13 07:35:10 -04:00
caa2073d48 improve support for external login roles 2024-09-13 07:34:57 -04:00
d9466fe4bc Fix issue in toggle edit mode 2024-09-13 14:15:34 +03:00
72e623a3a7 Merge pull request #4606 from sbwalker/dev
fix #4598 - user experience improvements for file upload
2024-09-12 14:04:48 -04:00
69bc06685f fix #4598 - user experience improvements for file upload 2024-09-12 14:04:35 -04:00
bf175984f3 Merge remote-tracking branch 'origin/dev' into dev 2024-09-11 18:23:41 -07:00
4d4a7bfd0d Merge pull request #4605 from sbwalker/dev
fix #4600 - filter user settings in API layer
2024-09-11 17:21:24 -04:00
044cee30a5 fix #4600 - filter user settings in API layer 2024-09-11 17:21:12 -04:00
3c0c5aed60 Add null operator in ModuleState.ModuleDefinition 2024-09-11 02:09:02 +03:00
e194971727 Merge pull request #4597 from sbwalker/dev
fix #4503 - ensure all state is initialized before rendering
2024-09-09 09:17:40 -04:00
bbe85def23 fix #4503 - ensure all state is initialized before rendering 2024-09-09 09:17:27 -04:00
9c2d53f2ae Merge pull request #4596 from sbwalker/dev
fix #4575 - add support for DateOnly and TimeOnly columns in migrations
2024-09-08 11:43:50 -04:00
1cf36e2156 fix #4575 - add support for DateOnly and TimeOnly columns in migrations 2024-09-08 11:43:36 -04:00
ce96e309af Merge pull request #4594 from sbwalker/dev
reverting #4507 - permission grid
2024-09-06 11:57:38 -04:00
9ea7dc8b3c reverting #4507 2024-09-06 11:57:15 -04:00
282242efd2 Merge pull request #4593 from mdmontesinos/dev
Fix IconOnly not working
2024-09-06 10:47:45 -04:00
351ba22d64 Fix IconOnly not working 2024-09-06 16:35:44 +02:00
2916625747 Merge pull request #4588 from thabaum/patch-8
Update MySQL Project File Version - Prepare for 5.2.2
2024-09-05 10:32:54 -04:00
744dbeb7a3 Prepare for 5.2.2 2024-09-04 17:43:20 -07:00
fbb3c309ee Merge pull request #4583 from sbwalker/dev
prepare for 5.2.2
2024-09-02 12:09:24 -04:00
473c265bac prepare for 5.2.2 2024-09-02 12:09:06 -04:00
01b06c2c3c Merge pull request #4582 from sbwalker/dev
remove reference to HttpContext as it is not used
2024-09-02 11:08:38 -04:00
8b1f95c743 remove reference to HttpContext as it is not used 2024-09-02 11:08:25 -04:00
ff5dbec579 Merge pull request #4578 from sbwalker/dev
fix issue adding existing user to a new site
2024-08-29 17:53:27 -04:00
9620c5a98f fix issue adding existing user to a new site 2024-08-29 17:53:11 -04:00
7e849a2e95 Merge pull request #4577 from zyhfish/task/update-bootstrap-reference 2024-08-29 09:35:32 -04:00
Ben
229aed306e update bootstrap reference. 2024-08-29 19:41:27 +08:00
d718969cbd Merge pull request #4574 from zyhfish/task/display-upgrade-progress
display the upgrade progress.
2024-08-28 21:22:08 -04:00
8593f4c3a9 Merge pull request #4576 from sbwalker/dev
improve developer experience for Url helper methods
2024-08-28 21:21:57 -04:00
d4f71d5026 improve developer experience for Url helper methods 2024-08-28 21:21:41 -04:00
a683a5f206 Merge pull request #4573 from leigh-pointer/AddExisitng
Fix for Deleted Modules showing in AddExisting & CopyExisting dropdow…
2024-08-28 21:17:54 -04:00
Ben
7917cc3eb5 display the upgrade progress. 2024-08-28 21:48:40 +08:00
6cd7ca755e Fix for Deleted Modules showing in AddExisting & CopyExisting dropdown #4572 2024-08-28 10:57:42 +02:00
fb7dfdc800 Merge pull request #4571 from sbwalker/dev
provide better support for AllowTextInput on Search component
2024-08-27 12:16:50 -04:00
e096af320f provide better support for AllowTextInput on Search component 2024-08-27 12:16:36 -04:00
65bff8f511 Merge pull request #4570 from sbwalker/dev
allow progress indicator to be displayed in search results
2024-08-27 10:22:53 -04:00
ca19d8a842 allow progress indicator to be displayed in search results 2024-08-27 10:22:41 -04:00
cde08c64ce Update README.md 2024-08-26 17:00:07 -04:00
3d6d7b7cb9 Update README.md 2024-08-26 16:56:08 -04:00
a56ef6f398 Update README.md 2024-08-26 16:55:30 -04:00
5a38b6614d Update README.md 2024-08-26 16:54:45 -04:00
3d5c44e8aa Update README.md 2024-08-26 16:53:25 -04:00
46a68bd5e7 Update README.md 2024-08-26 16:51:53 -04:00
c77ded51a9 Merge pull request #4568 from sbwalker/dev
fix #4559 - prevednt Log fields from exceeding column length
2024-08-26 12:05:05 -04:00
59bd9fdc22 fix #4559 - prevednt Log fields from exceeding column length 2024-08-26 12:04:50 -04:00
b81941394c Merge pull request #4567 from sbwalker/dev
change parameter name to AllowTextInput for clarity
2024-08-26 10:47:29 -04:00
197d5ca1f2 change parameter name to AllowTextInput for clarity 2024-08-26 10:47:18 -04:00
97eb986fa6 Merge pull request #4566 from sbwalker/dev
fix Search theme control so that it checks if search is enabled for site, and include AllowInput parameter to control input textbox
2024-08-26 10:41:39 -04:00
aba3110e31 fix Search theme control so that it checks if search is enabled for site, and include AllowInput parameter to control input textbox 2024-08-26 10:41:22 -04:00
51c27be236 Merge pull request #4565 from sbwalker/dev
fix #4562 - module template issue caused by gitignore
2024-08-26 09:41:01 -04:00
592255284f fix #4562 - module template issue caused by gitignore 2024-08-26 09:38:10 -04:00
82fd41dd4c Merge pull request #4555 from sbwalker/dev
add readme.md to Oqtane nuget packages
2024-08-22 14:10:42 -04:00
4ae8df9652 add readme.md to Oqtane nuget packages 2024-08-22 14:09:59 -04:00
759b19e444 Update README.md 2024-08-22 12:23:21 -04:00
36f705e46f Update README.md 2024-08-22 12:23:09 -04:00
1f0347682e Merge pull request #4553 from oqtane/master
5.2.1 Release
2024-08-22 12:15:38 -04:00
1aee385679 Merge pull request #4552 from oqtane/dev
5.2.1 Release
2024-08-22 12:15:15 -04:00
5dd8191692 Merge pull request #4551 from sbwalker/dev
fixed required field validation in Search Results Settings
2024-08-22 12:10:34 -04:00
b6422f9b80 fixed required field validation in Search Results Settings 2024-08-22 12:10:15 -04:00
a6c2c9c92f Merge pull request #4550 from sbwalker/dev
use localized Yes/No values when displaying Site Urls Default? option
2024-08-22 10:53:43 -04:00
247c573a6e use localized Yes/No values when displaying Site Urls Default? option 2024-08-22 10:53:27 -04:00
aa435d6e94 Merge pull request #4548 from sbwalker/dev
fix #4546 - handle cache invalidation for site deletion
2024-08-22 08:26:44 -04:00
f6858c221b fix #4546 - handle cache invalidation for site deletion 2024-08-22 08:26:29 -04:00
437aa4510b Merge pull request #4547 from sbwalker/dev
fix #4545 - Site Settings - UI Component Settings changes not refreshed after saving
2024-08-22 08:09:18 -04:00
430572fb32 fix #4545 - Site Settings - UI Component Settings changes not refreshed after saving 2024-08-22 08:08:55 -04:00
9f0b755d6f Merge pull request #4543 from sbwalker/dev
fix login redirect issue in sub-site where user has navigated directly to login page
2024-08-21 15:17:47 -04:00
66acb55a57 fix login redirect issue in sub-site where user has navigated directly to login page 2024-08-21 15:17:29 -04:00
f936d4c36e Merge pull request #4542 from sbwalker/dev
fix #4536 - deleted modules appearing in Page Management - Modules panel
2024-08-20 16:18:45 -04:00
c3ddb8df56 fix #4536 - deleted modules appearing in Page Management - Modules panel 2024-08-20 16:18:31 -04:00
81ce920e69 Merge pull request #4541 from sbwalker/dev
fix issues in default template for Interactive Client (WebAssembly) scenarios
2024-08-20 15:34:05 -04:00
3cb875d139 fix issues in default template for Interactive Client (WebAssembly) scenarios 2024-08-20 15:33:46 -04:00
fdb217d5c6 Merge pull request #4540 from sbwalker/dev
set BaseAddress for IHttpClientFactory
2024-08-20 15:22:39 -04:00
085cae3b5f set BaseAddress for IHttpClientFactory 2024-08-20 15:22:27 -04:00
e2f99a1554 Merge pull request #4538 from sbwalker/dev
fix #4498 build ServerState Assemblies collection in a more thread safe manner
2024-08-20 14:03:02 -04:00
aee0c27da7 fix #4498 build ServerState Assemblies collection in a more thread safe manner 2024-08-20 14:02:37 -04:00
accbf4ad8b Merge pull request #4535 from sbwalker/dev
use existing SiteKey
2024-08-20 08:35:36 -04:00
0ac1901f6b use existing SiteKey 2024-08-20 08:35:23 -04:00
c0a0deea78 Merge pull request #4532 from sbwalker/dev
ensure form name is unique in ActionDialog
2024-08-19 16:58:48 -04:00
e3f099441c ensure form name is unique in ActionDialog 2024-08-19 16:58:33 -04:00
840dd25cd1 Merge pull request #4530 from sbwalker/dev
fix issues with ActionDialog in static rendering
2024-08-19 09:34:23 -04:00
a493969f9b fix issues with ActionDialog in static rendering 2024-08-19 09:34:08 -04:00
cca42a10a1 Merge pull request #4529 from sbwalker/dev
fix CSS
2024-08-19 09:05:00 -04:00
2f4aa98c3c fix CSS 2024-08-19 09:04:46 -04:00
175cb9588c Merge pull request #4524 from sbwalker/dev
prevent scroll position from resetting to top of page when querystring or hash changes
2024-08-16 15:01:43 -04:00
a8976e7559 prevent scroll position from resetting to top of page when querystring or hash changes 2024-08-16 15:01:25 -04:00
b663528fb0 Merge pull request #4521 from sbwalker/dev
add ability to extract zip file contents in File Management
2024-08-14 15:54:30 -04:00
1a2ad55677 add ability to extract zip file contents in File Management 2024-08-14 15:54:13 -04:00
513d2a88c0 Merge pull request #4520 from sbwalker/dev
move HtmlText caching from repository to service layer
2024-08-14 10:01:11 -04:00
57ef4c0396 move HtmlText caching from repository to service layer 2024-08-14 10:00:56 -04:00
e9599ca2f4 Merge pull request #4519 from sbwalker/dev
fix #4517 - index error due to duplicate records on upgrade
2024-08-14 08:19:28 -04:00
2fc6dbc222 fix #4517 - index error due to duplicate records on upgrade 2024-08-14 08:19:14 -04:00
225933c442 Merge pull request #4518 from sbwalker/dev
update nuspec files for 5.2.1
2024-08-14 08:12:46 -04:00
36e2f048d7 update nuspec files for 5.2.1 2024-08-14 08:12:31 -04:00
0e158bce59 Merge pull request #4515 from thabaum/update-version-5.2.1-and-dependencies
Fixes #4514 - Update version 5.2.1 and dependencies
2024-08-14 07:58:55 -04:00
202201fd31 Merge pull request #4516 from ijaz-saeed/dev
search settings translation entry
2024-08-14 07:55:11 -04:00
4f8c928f44 search settings translation entry 2024-08-14 11:57:06 +05:00
ada062cf00 Update version to 5.2.1 and dependencies 2024-08-13 15:53:45 -07:00
32dc12912a Update dependencies 2024-08-13 15:51:49 -07:00
671d21adf4 Update dependencies 2024-08-13 15:50:56 -07:00
4e3f8e4b67 Update dependencies 2024-08-13 15:49:15 -07:00
586bb62073 Update version to 5.2.1 and dependencies 2024-08-13 15:48:32 -07:00
151bf83ab1 Update version to 5.2.1 and dependencies 2024-08-13 15:46:45 -07:00
8c618edb5a Update version to 5.2.1 2024-08-13 15:45:23 -07:00
6d6b0cf8c9 Update version to 5.2.1 and dependencies 2024-08-13 15:44:45 -07:00
c610608890 Update version to 5.2.1 and dependencies 2024-08-13 15:43:27 -07:00
28da61daab Update version 5.2.1 and dependencies 2024-08-13 15:39:15 -07:00
cbfc90f60b Update Version To 5.2.1 and Dependencies To Latest 2024-08-13 15:36:20 -07:00
fec92959d4 Merge pull request #4513 from sbwalker/dev
move folder permissions grid to dedicated tab for consistency
2024-08-13 16:48:34 -04:00
bb00d81eba move folder permissions grid to dedicated tab for consistency 2024-08-13 16:48:22 -04:00
bb55644c06 Merge pull request #4512 from sbwalker/dev
improve file name and extension validation
2024-08-12 17:02:22 -04:00
16215847cd improve file name and extension validation 2024-08-12 17:02:07 -04:00
8ee9aed817 Merge pull request #4509 from sbwalker/dev
improve SettingService
2024-08-12 10:20:56 -04:00
515c6402b9 improve SettingService 2024-08-12 10:20:44 -04:00
d1b94ec203 Merge pull request #4507 from leigh-pointer/Permissions-4503
Fix for #4503 Module Custom Permissions not being shown
2024-08-10 17:54:51 -04:00
a037d9167e Fix for #4503 Module Custom Permissions not being shown 2024-08-10 21:25:32 +02:00
3054d33e62 Merge pull request #4493 from thabaum/set-samesite-lax-visitor-culture-cookies
Fix #4492: Updates Culture and Visitor cookies to use "Lax" SameSite and Secure Cookie Options
2024-08-10 14:08:01 -04:00
6651e641e1 Merge pull request #4505 from sbwalker/dev
add search reindex capability
2024-08-10 10:02:08 -04:00
35f873a342 add search reindex capability 2024-08-10 10:01:52 -04:00
2d03ff38a1 Merge pull request #4504 from sbwalker/dev
replace dynamic query with linq
2024-08-09 17:16:30 -04:00
44a3db417b replace dynamic query with linq 2024-08-09 17:16:17 -04:00
f9ca702a12 Merge pull request #4502 from sbwalker/dev
fix #4499 - page modules not loaded properly
2024-08-09 13:11:31 -04:00
4073ff38eb fix #4499 - page modules not loaded properly 2024-08-09 13:11:19 -04:00
f0e2c9f1b6 Merge pull request #4501 from sbwalker/dev
eliminate database call for authenticated users
2024-08-09 13:00:35 -04:00
cf040f51b5 eliminate database call for authenticated users 2024-08-09 13:00:20 -04:00
dcf919fb36 Adds AntiForgery Cookie setting options.Cookie.HttpOnly = true; 2024-08-08 12:24:42 -07:00
aa19b81a68 Merge pull request #4487 from leigh-pointer/TemplateUpdate
Update Theme Template to Bootstrap 5.3.3
2024-08-08 14:44:27 -04:00
db8d77365c Merge pull request #4479 from pollux/patch-1
Fix admin/pages not showing 404 for unauthorized users
2024-08-08 14:42:08 -04:00
280eaea84a Merge pull request #4497 from sbwalker/dev
improve search result performance and relevancy
2024-08-08 14:11:43 -04:00
340ef46469 improve search result performance and relevancy 2024-08-08 14:11:27 -04:00
a5f8651941 Revert previous cookie HttpOnly option 2024-08-07 16:24:18 -07:00
8a18ee548e Merge pull request #4494 from sbwalker/dev
add missing indexes
2024-08-07 16:55:48 -04:00
ef791aa22a add missing indexes 2024-08-07 16:55:35 -04:00
4bdf2e1cc0 Update AntiForgery Token Cookie Option to HTTPOnly = true; 2024-08-07 13:21:18 -07:00
ffa0ca9379 Updates Culture and Visitor cookies to use "Lax" SameSite and Secure cookie options 2024-08-07 11:52:53 -07:00
d3b3d46fc1 Return to standard Bootstrap 2024-08-07 20:01:41 +02:00
7e7dd8efa9 Update Theme Template to Bootstrap 5.3.3 2024-08-07 10:34:40 +02:00
b4506f1133 Merge pull request #4483 from sbwalker/dev
include "://" on default Alias Protocol for consistency
2024-08-05 07:51:07 -04:00
7350c79113 include "://" on default Alias Protocol for consistency 2024-08-05 07:50:54 -04:00
5f7b60d3f4 Merge pull request #4480 from leigh-pointer/Log2xHttp
Removed the extra "://" from the Log Manager
2024-08-05 07:50:06 -04:00
266495a611 Removed the extra ";//" from the Log Manager
{alias.Protocol} return with ";//"
2024-08-03 12:26:33 +02:00
f5b4a7e77b Fix admin/pages not showing 404 for unauthorized users 2024-08-02 11:49:55 +02:00
51ed0f6487 Merge pull request #4472 from sbwalker/dev
fix #4471 - search pages not being added on upgrade
2024-07-27 09:51:22 -04:00
bd70def18a fix #4471 - search pages not being added on upgrade 2024-07-27 09:51:02 -04:00
93a9cf3b31 Update README.md 2024-07-25 13:22:02 -04:00
751287999f Update README.md 2024-07-25 11:57:24 -04:00
d433850cbf Merge pull request #4469 from oqtane/master
5.2.0 release
2024-07-25 11:49:12 -04:00
c93e70e2dc Merge pull request #4468 from oqtane/dev
5.2.0 release
2024-07-25 11:48:50 -04:00
a129dd989a Merge pull request #4467 from sbwalker/dev
remove Settings button logic from QuillJS text editor interop
2024-07-25 11:13:19 -04:00
40999c3ff4 remove Settings button logic from QuillJS text editor interop 2024-07-25 11:12:58 -04:00
18a01d672c Merge pull request #4466 from sbwalker/dev
resolve localization issue in ActionDialog
2024-07-25 09:24:34 -04:00
3648f99920 resolve localization issue in ActionDialog 2024-07-25 09:24:21 -04:00
e823412f56 Merge pull request #4465 from leigh-pointer/NotifyLogging
Small Notification Job update
2024-07-25 08:47:00 -04:00
d090f446c9 Small Notification Job update
This update adds the NotificationId to the log to help track down any errors.
2024-07-25 13:11:05 +02:00
19985d1742 Merge pull request #4464 from sbwalker/dev
resolve issue with default Blazor theme
2024-07-24 13:00:03 -04:00
ab52251116 resolve issue with default Blazor theme 2024-07-24 12:59:40 -04:00
acb6c0187c Merge pull request #4461 from sbwalker/dev
add missing localization for Search Results Settings
2024-07-23 14:44:30 -04:00
90f9c24720 add missing localization for Search Results Settings 2024-07-23 14:44:14 -04:00
415bec4646 Merge pull request #4460 from sbwalker/dev
prevent breaking change for interactive components referencing PageState.Pages
2024-07-23 12:48:13 -04:00
5559f20511 prevent breaking change for interactive components referencing PageState.Pages 2024-07-23 12:47:54 -04:00
6ea3399829 Merge pull request #4458 from sbwalker/dev
use PageState.Site.Settings rather than reloading settings from database
2024-07-23 07:45:32 -04:00
9e3df97737 use PageState.Site.Settings rather than reloading settings from database 2024-07-23 07:45:13 -04:00
0ac40a4d77 Merge pull request #4456 from leigh-pointer/SixLabors.ImageSharp
ENH update to 3.1.5 #4455 Vulnerabilities detected in 3.1.4
2024-07-23 07:13:45 -04:00
24bf5e8102 Merge pull request #4454 from mdmontesinos/dev
fix: set Rich active tab for Quill text editor
2024-07-23 07:13:24 -04:00
56eebb03c7 Merge pull request #4457 from sbwalker/dev
change IsEffectiiveOrExpired to IsEffectiveAndNotExpired
2024-07-23 07:11:31 -04:00
1cd4d6d0df change IsEffectiiveOrExpired to IsEffectiveAndNotExpired 2024-07-23 07:08:26 -04:00
22d4a8232a ENH update to 3.1.5 #4455 Vulnerabilities detected in 3.1.4 2024-07-23 13:03:03 +02:00
48076c25bf fix: set Rich active tab for Quill text editor 2024-07-23 09:18:58 +02:00
478a308e73 Merge pull request #4453 from sbwalker/dev
fix #4284 - handle user role effective and expiry date
2024-07-22 21:09:52 -04:00
8ca2f0a49f fix #4284 - handle user role effective and expiry date 2024-07-22 21:09:35 -04:00
4a35d7364b Merge pull request #4452 from sbwalker/dev
remove ITextEditorProvider interface
2024-07-22 13:31:39 -04:00
8b2e55a969 remove ITextEditorProvider interface 2024-07-22 13:31:24 -04:00
9b14b70687 Merge pull request #4451 from sbwalker/dev
fix #4450 QuillJSTextEditor settings
2024-07-22 08:11:24 -04:00
1c01087eda fix #4450 QuillJSTextEditor settings 2024-07-22 08:11:09 -04:00
5137e5a301 Merge pull request #4449 from sbwalker/dev
add documentation
2024-07-21 09:19:00 -04:00
0fea8365b8 add documentation 2024-07-21 09:18:41 -04:00
116a615b84 Merge pull request #4448 from sbwalker/dev
change Ignore Paths to Ignore Pages
2024-07-21 09:10:23 -04:00
ef272dd6a8 change Ignore Paths to Ignore Pages 2024-07-21 09:10:01 -04:00
4462ae9cae Merge pull request #4447 from sbwalker/dev
moved Search Provider setting to Search Settings
2024-07-21 08:59:41 -04:00
66ffad0b4e moved Search Provider setting to Search Settings 2024-07-21 08:59:23 -04:00
81e0fc940c Merge pull request #4446 from sbwalker/dev
add Search Provider to Site Settings
2024-07-21 08:49:38 -04:00
3e8794db1b add Search Provider to Site Settings 2024-07-21 08:49:18 -04:00
617622d4d8 Merge pull request #4445 from sbwalker/dev
add Functionality section to Site Settings
2024-07-21 08:16:10 -04:00
a4240f972b add Functionality section to Site Settings 2024-07-21 08:15:51 -04:00
42feff8882 Merge pull request #4443 from sbwalker/dev
more localization changes
2024-07-20 21:52:56 -04:00
70edd9686f more localization changes 2024-07-20 21:52:41 -04:00
d298cb2e1c Merge pull request #4442 from sbwalker/dev
fix localization for QuillJSTextEditor
2024-07-20 21:43:57 -04:00
3fef3f2dc3 fix localization for QuillJSTextEditor 2024-07-20 21:43:38 -04:00
c85f9d6ae4 Merge pull request #4440 from sbwalker/dev
remove unecessary using
2024-07-20 19:39:58 -04:00
85e7ac7cd7 remove unecessary using 2024-07-20 19:39:45 -04:00
5c7db61a7e Merge pull request #4439 from sbwalker/dev
improve validation of seach content
2024-07-20 19:18:06 -04:00
497f9ca0b1 improve validation of seach content 2024-07-20 19:17:47 -04:00
0c50f7a322 Merge pull request #4438 from sbwalker/dev
allow page-script to support exterrnal JavaScript
2024-07-19 15:42:35 -04:00
740bcbd12c allow page-script to support exterrnal JavaScript 2024-07-19 15:42:20 -04:00
c92be4f270 Merge pull request #4437 from sbwalker/dev
revert modification done for testing purposes only
2024-07-19 15:31:24 -04:00
e2a7271ab2 revert modification done for testing purposes only 2024-07-19 15:31:11 -04:00
64766713fa Merge pull request #4436 from sbwalker/dev
add ability to manage search results settings
2024-07-19 12:56:14 -04:00
59bba83b1d add ability to manage search results settings 2024-07-19 12:55:59 -04:00
8ac1217165 Merge pull request #4435 from sbwalker/dev
allow <style> tags to be injected using HeadContent
2024-07-18 15:01:43 -04:00
5443629ec5 allow <style> tags to be injected using HeadContent 2024-07-18 15:01:20 -04:00
8f9b41cb62 Merge pull request #4434 from sbwalker/dev
use JSInterop for loading QuillJSTextEditor stylesheet
2024-07-18 13:17:58 -04:00
7df5eba775 use JSInterop for loading QuillJSTextEditor stylesheet 2024-07-18 13:17:23 -04:00
e29c6ac593 Merge pull request #4433 from sbwalker/dev
improve search user experience
2024-07-18 11:50:07 -04:00
4f3190bf73 improve search user experience 2024-07-18 11:49:42 -04:00
f0878fccb5 Merge pull request #4432 from sbwalker/dev
fix ISearchable implementation in default module template
2024-07-18 11:10:39 -04:00
b0e121a53f fix ISearchable implementation in default module template 2024-07-18 11:10:24 -04:00
eda48ab0e6 Merge pull request #4431 from sbwalker/dev
QuillJSTextEditor setting improvements
2024-07-18 11:03:13 -04:00
6a1014d8c1 QuillJSTextEditor setting improvements 2024-07-18 11:01:01 -04:00
e0f87315bc Merge pull request #4430 from sbwalker/dev
set Prerender on Login component
2024-07-17 20:52:28 -04:00
f2c5dca5e7 set Prerender on Login component 2024-07-17 20:52:13 -04:00
3c435a804f Merge pull request #4429 from sbwalker/dev
remove hardcoded names when using GetInterface()
2024-07-17 19:53:06 -04:00
7ee6775251 remove hardcoded names when using GetInterface() 2024-07-17 19:52:44 -04:00
98adc2ecc1 Merge pull request #4428 from sbwalker/dev
allow search content permissions to support roles
2024-07-17 19:34:34 -04:00
45afbbdac6 allow search content permissions to support roles 2024-07-17 19:34:19 -04:00
a18260747b Merge pull request #4427 from sbwalker/dev
add Refresh option for Job Logs
2024-07-17 17:12:38 -04:00
0c80e28754 add Refresh option for Job Logs 2024-07-17 17:12:24 -04:00
719bc374ac Merge pull request #4426 from sbwalker/dev
add localization to search settings
2024-07-17 16:34:16 -04:00
d7a290c595 add localization to search settings 2024-07-17 16:34:02 -04:00
4b2bd33baa Merge pull request #4425 from sbwalker/dev
update theme template to .NET 8.0.7
2024-07-17 16:23:30 -04:00
efbe4d697c update theme template to .NET 8.0.7 2024-07-17 16:23:15 -04:00
a8662bdb8b Merge pull request #4424 from sbwalker/dev
use Task.FromResult()
2024-07-17 16:22:16 -04:00
d822225465 use Task.FromResult() 2024-07-17 16:22:01 -04:00
c6373ef582 Merge pull request #4423 from sbwalker/dev
update module template to .NET 8.0.7
2024-07-17 16:17:30 -04:00
5a2af6d0f9 update module template to .NET 8.0.7 2024-07-17 16:17:10 -04:00
52000d6a41 Merge pull request #4422 from sbwalker/dev
update to .NET 8.0.7
2024-07-17 15:10:54 -04:00
befa13eaf2 update to .NET 8.0.7 2024-07-17 15:10:40 -04:00
4ac68a81a3 Merge pull request #4421 from sbwalker/dev
search optimizations
2024-07-17 13:58:03 -04:00
71e472f330 search optimizations 2024-07-17 13:57:47 -04:00
ada8809ec0 Merge pull request #4420 from oqtane/revert-4417-dev
Revert "disable prerendering by default for static rendered sites"
2024-07-17 12:13:35 -04:00
25ea518266 Revert "revert #4250 which disabled prerendering by default for static rendered sites" 2024-07-17 12:12:58 -04:00
f3720c3b94 Merge pull request #4419 from sbwalker/dev
improve PageState trimming
2024-07-17 11:53:22 -04:00
b942a84b15 improve PageState trimming 2024-07-17 11:53:04 -04:00
574ca90229 Merge pull request #4418 from sbwalker/dev
add missing properties to Clone method
2024-07-17 11:34:15 -04:00
5610a14e49 add missing properties to Clone method 2024-07-17 11:34:01 -04:00
c5bc62e6df Merge pull request #4417 from sbwalker/dev
revert #4250 which disabled prerendering by default for static rendered sites
2024-07-17 11:22:18 -04:00
e9f6a85cad revert #4250 which disabled prerendering by default for static rendered sites 2024-07-17 11:20:27 -04:00
8921011b27 Merge pull request #4416 from sbwalker/dev
performance improvement in Control Panel to only load list of pages when necessary
2024-07-17 11:13:46 -04:00
90ef3f6c94 performance improvement in Control Panel to only load list of pages when necessary 2024-07-17 11:13:27 -04:00
e84c75f4a8 Merge pull request #4414 from pollux/patch-1
Update Oqtane.Shared.csproj
2024-07-17 11:09:59 -04:00
68e20cd860 Merge pull request #4415 from sbwalker/dev
include Search Settings
2024-07-17 11:09:00 -04:00
76bdcea4b1 include Search Settings 2024-07-17 11:08:43 -04:00
f758cb7e6e Update Oqtane.Shared.csproj
Fixes CVE-2024-30105
2024-07-17 12:46:49 +02:00
1108477810 Merge pull request #4413 from sbwalker/dev
make SearchResults API consistent with other core APIs
2024-07-16 16:55:09 -04:00
deb6a9e51c make SearchResults API consistent with other core APIs 2024-07-16 16:54:55 -04:00
9660f20b87 Merge pull request #4412 from sbwalker/dev
performance optimization to mitigate page bloat caused by Blazor serializing/encrypting state when crossing render mode boundaries
2024-07-16 16:21:57 -04:00
4d26468ede performance optimization to mitigate page bloat caused by Blazor serializing/encrypting state when crossing render mode boundaries 2024-07-16 16:21:35 -04:00
125a0979d5 Merge pull request #4408 from sbwalker/dev
removed unused constants
2024-07-15 10:11:31 -04:00
98bdfd3dbe removed unused constants 2024-07-15 10:11:14 -04:00
ea72880e74 Merge pull request #4407 from sbwalker/dev
resolve security issue in Search
2024-07-15 10:08:04 -04:00
17fec7d6e1 resolve security issue in Search 2024-07-15 10:07:48 -04:00
d9de64604e Merge pull request #4406 from sbwalker/dev
fix #4401 - avoid mutating Site object in cache
2024-07-15 08:37:46 -04:00
6275ab23ff fix #4401 - avoid mutating Site object in cache 2024-07-15 08:37:23 -04:00
8c0271643d Merge pull request #4405 from sbwalker/dev
testing search indexing of files
2024-07-13 09:28:25 -04:00
c3f041dc87 testing search indexing of files 2024-07-13 09:28:02 -04:00
80e5e84341 Merge pull request #4404 from sbwalker/dev
only include pages in index if they do not have any modules
2024-07-12 10:53:25 -04:00
938eee80a9 only include pages in index if they do not have any modules 2024-07-12 10:52:58 -04:00
7abc2289de Merge pull request #4402 from sbwalker/dev
search modifications
2024-07-12 10:33:34 -04:00
bb79b9ed74 search modifications 2024-07-12 10:33:17 -04:00
1e89a8625c Merge pull request #4400 from sbwalker/dev
fix #4397 - remove incorrect help text
2024-07-11 13:39:19 -04:00
90b0f04b3c fix #4397 - remove incorrect help text 2024-07-11 13:39:05 -04:00
0f019cd9b6 Merge pull request #4399 from sbwalker/dev
fix #4398 - InputList component not handling scenario where input is reset to nothing
2024-07-11 13:36:14 -04:00
1209739398 fix #4398 - InputList component not handling scenario where input is reset to nothing 2024-07-11 13:35:46 -04:00
b99db2b353 Merge pull request #4394 from sbwalker/dev
more Site Settings for search configuration
2024-07-08 16:59:10 -04:00
f057688e7d more Site Settings for search configuration 2024-07-08 16:58:55 -04:00
12ae2d0c76 Merge pull request #4393 from sbwalker/dev
site settings to configure indexing
2024-07-08 14:33:54 -04:00
9d91d5a127 site settings to configure indexing 2024-07-08 14:33:16 -04:00
6015f0887a Merge pull request #4389 from sbwalker/dev
fix #4384 - app_offline https link
2024-07-06 08:38:52 -04:00
d4c473d7b3 fix #4384 - app_offline https link 2024-07-06 08:38:33 -04:00
2f3978deed Merge pull request #4374 from zyhfish/task/fix-issue-4358
Fix #4358: RichTextEditor Provider Abstraction.
2024-07-06 08:32:19 -04:00
34af53a15b Merge branch 'dev' into task/fix-issue-4358 2024-07-06 08:32:10 -04:00
a4eb3d7a0b Merge pull request #4388 from sbwalker/dev
search refactoring
2024-07-06 07:58:35 -04:00
5b46dd7293 search refactoring 2024-07-06 07:58:04 -04:00
4b17847ea5 Merge remote-tracking branch 'oqtane/dev' into dev 2024-07-05 21:53:14 -07:00
Ben
acbe000f97 avoid race condition issue. 2024-07-03 17:59:40 +08:00
Ben
599071b68b avoid race condition issue. 2024-07-03 17:07:47 +08:00
Ben
2bacee919d update the settings UI. 2024-07-03 12:26:36 +08:00
50d35e4196 Merge pull request #4383 from sbwalker/dev
remove unnecessary using
2024-07-02 15:53:40 -04:00
e321998b85 remove unnecessary using 2024-07-02 15:53:26 -04:00
340c02b2af Merge pull request #4382 from sbwalker/dev
remove unnecessary database call to GetPage
2024-07-02 15:45:59 -04:00
69a295fe57 remove unnecessary database call to GetPage 2024-07-02 15:45:44 -04:00
64830aae9f Merge pull request #4381 from sbwalker/dev
use PageModule in ISearchable
2024-07-02 14:50:42 -04:00
8969b1273f use PageModule in ISearchable 2024-07-02 14:50:26 -04:00
475faf7943 Merge pull request #4380 from sbwalker/dev
fix #4375 - deleted pages not being filtered
2024-07-02 11:05:42 -04:00
45b1d405a6 fix #4375 - deleted pages not being filtered 2024-07-02 11:05:29 -04:00
Ben
f60c8078e4 cleanup the code. 2024-07-02 09:55:38 +08:00
Ben
6701e49f9a move the editor settings into editor self control. 2024-07-02 09:50:53 +08:00
0efb3c3284 Merge pull request #4376 from sbwalker/dev
ensure UniqueKey is unique by including TenantId and SiteId
2024-07-01 16:20:15 -04:00
aaf3cdfdac ensure UniqueKey is unique by including TenantId and SiteId 2024-07-01 16:19:58 -04:00
Ben
e00c261777 Fix #4358: RichTextEditor Provider Abstraction. 2024-07-01 17:11:26 +08:00
1eafed755d Merge pull request #4372 from sbwalker/dev
provide default Permissions value
2024-06-28 17:31:48 -04:00
7f6a08ae50 provide default Permissions value 2024-06-28 17:31:33 -04:00
9901816fb9 Merge pull request #4371 from sbwalker/dev
ensure ModuleDefinition exists
2024-06-28 17:26:58 -04:00
3cbe6c1e95 ensure ModuleDefinition exists 2024-06-28 17:26:46 -04:00
679c99274e Merge pull request #4370 from sbwalker/dev
change EntityId to string
2024-06-28 16:25:11 -04:00
b6fa0f1ff6 change EntityId to string 2024-06-28 16:24:56 -04:00
9ff64b95e1 Merge pull request #4369 from sbwalker/dev
modify query property names
2024-06-28 16:15:42 -04:00
3a9885abd8 modify query property names 2024-06-28 16:15:28 -04:00
503210942c Merge pull request #4368 from sbwalker/dev
breaking search modifications into smaller PRs
2024-06-28 15:44:11 -04:00
0178e015e3 breaking search modifications into smaller PRs 2024-06-28 15:43:54 -04:00
7604992c35 Merge pull request #4361 from 2sic-forks/bug/4360
fix docfx build issues #4360
2024-06-28 09:01:45 -04:00
ee9e551788 Merge pull request #4365 from sbwalker/dev
reposition Search input in themes
2024-06-27 17:18:54 -04:00
22063248ca reposition Search input in themes 2024-06-27 17:18:41 -04:00
e4ce18b35c Merge pull request #4364 from sbwalker/dev
eager load Page associated to PageModule
2024-06-27 17:05:35 -04:00
532890674e eager load Page associated to PageModule 2024-06-27 17:05:22 -04:00
791a3b67e6 fix docfx build issues 2024-06-27 19:47:13 +02:00
84b560ef29 Merge pull request #4356 from sbwalker/dev
fix #4353 - add defensive logic when sending notifications and improve performance
2024-06-26 09:09:29 -04:00
03f081f3f4 fix #4353 - add defensive logic when sending notifications and improve performance 2024-06-26 09:09:06 -04:00
08213ae86e Merge pull request #4352 from sbwalker/dev
fix #4349 - adding module in subsite in Interactive render mode
2024-06-24 16:27:35 -04:00
73abc511a8 fix #4349 - adding module in subsite in Interactive render mode 2024-06-24 16:26:55 -04:00
1c943cc259 Merge pull request #4350 from sbwalker/dev
fix #4339 - add page not redirecting to correct url in subsite
2024-06-24 11:00:01 -04:00
af62a89a6b fix #4339 - add page not redirecting to correct url in subsite 2024-06-24 10:59:34 -04:00
af7ca5b897 Merge pull request #4338 from sbwalker/dev
use List instead of IList, remove "List" from method names. remove unnecessary using statements
2024-06-11 10:39:03 -04:00
27356ef747 use List instead of IList, remove "List" from method names. remove unnecessary using statements 2024-06-11 10:38:44 -04:00
b27f80ef87 Merge pull request #4337 from leigh-pointer/SearchResultsCat
Updated SearchResults Categories to Admin
2024-06-11 10:28:56 -04:00
b4aa73fc64 Updated SearchResults Categories to Admin 2024-06-11 15:21:12 +02:00
bbf444572b Merge pull request #4336 from sbwalker/dev
removed IHttpContextAccessor as it shoudl not be used in Blazor,  fixed form handling, added Reset button to consistent with other framework search options, used SharedLocalizer, removed unused localization keys
2024-06-11 08:39:28 -04:00
b3706574de removed IHttpContextAccessor as it shoudl not be used in Blazor, fixed form handling, added Reset button to consistent with other framework search options, used SharedLocalizer, removed unused localization keys 2024-06-11 08:38:51 -04:00
83062f8bfb Merge pull request #4335 from sbwalker/dev
fix ISearchable method name in module template
2024-06-11 07:37:25 -04:00
1d7fcfdaa1 fix ISearchable method name in module template 2024-06-11 07:37:02 -04:00
58ab12b4cb Merge pull request #4328 from leigh-pointer/UpdateTemplates
Update Project Templates from 8.0.5 - 8.0.6
2024-06-11 07:35:02 -04:00
28c649629f Merge pull request #4334 from leigh-pointer/Bootstrap5.3.3
Upgrade to Bootstrap 5.3.3
2024-06-11 07:32:10 -04:00
5ec190225d Upgrade to Bootstrap 5.3.3
Oqtane ThemeInfo updated
Blazor Default.razor updated
2024-06-11 09:51:48 +02:00
0e0d404997 Update [Module]Manager.cs 2024-06-11 09:11:25 +02:00
3f16b908ca Merge remote-tracking branch 'upstream/dev' into UpdateTemplates 2024-06-11 08:49:28 +02:00
54549b261c Merge pull request #4333 from sbwalker/dev
change IList to List for consistency with rest of framework
2024-06-10 17:17:33 -04:00
8ce07ced9e change IList to List for consistency with rest of framework 2024-06-10 17:17:20 -04:00
37a5144e8f Merge pull request #4332 from sbwalker/dev
update app constant to 5.2.0
2024-06-10 16:50:45 -04:00
59fed7dda8 update app constant to 5.2.0 2024-06-10 16:50:33 -04:00
0a85a2868c Merge pull request #4331 from sbwalker/dev
fix issue with primary key on SearchContentWord table
2024-06-10 16:47:45 -04:00
0d493b3250 fix issue with primary key on SearchContentWord table 2024-06-10 16:47:32 -04:00
74530d4b1e Merge pull request #4330 from sbwalker/dev
remove unnecessary using statements
2024-06-10 16:23:10 -04:00
7548c52e21 remove unnecessary using statements 2024-06-10 16:22:58 -04:00
fa49844c64 Merge pull request #4329 from sbwalker/dev
remove List from method name to conform to Oqtane naming conventions
2024-06-10 16:17:19 -04:00
3508ae1e0a remove List from method name to conform to Oqtane naming conventions 2024-06-10 16:17:05 -04:00
f013ee64a2 Updated Module Template with ISearchable implementation 2024-06-10 22:10:39 +02:00
af3da7ca6e update to template.json files to align with Oqtane version 2024-06-10 21:54:44 +02:00
cb728f65b3 Update Project Templates from 8.0.5 - 8.0.6 2024-06-10 21:50:30 +02:00
af6af190cc Merge pull request #4325 from thabaum/update-package-dependences-v5.2.0
Fixes #4324: Updates package dependences and prepares v5.2.0 release
2024-06-10 15:06:08 -04:00
cbcc8455ca Merge pull request #4326 from sbwalker/dev
refactored to move AdminSiteTemplate out of SiteRepository
2024-06-10 14:55:21 -04:00
af35fb79fe refactored to move AdminSiteTemplate out of SiteRepository 2024-06-10 14:55:06 -04:00
0515aaa946 Prepare v5.2.0 Release 2024-06-10 10:48:46 -07:00
1d00330e7a Prepare v5.2.0 Release and Package Dependencies 2024-06-10 10:48:18 -07:00
3fa6dcea16 Prepare v5.2.0 Release and Package Dependencies 2024-06-10 10:47:03 -07:00
51425cac4a Prepare v5.2.0 Release 2024-06-10 10:44:40 -07:00
7ce61a5d2b Prepare v5.2.0 Release 2024-06-10 10:43:54 -07:00
5e5caa979b Prepare v5.2.0 Release 2024-06-10 10:43:19 -07:00
a719518f8f Prepare v5.2.0 Release 2024-06-10 10:42:57 -07:00
d0e5aef443 Prepare v5.2.0 Release 2024-06-10 10:42:32 -07:00
b82a811c33 Prepare v5.2.0 Release and Package Dependencies 2024-06-10 10:41:51 -07:00
3356dcf8f7 Prepare v5.2.0 Release 2024-06-10 10:41:10 -07:00
8f1cc26537 Prepare v5.2.0 Release 2024-06-10 10:40:50 -07:00
27dafa83ac Prepare Update v5.2.0 Package Dependencies 2024-06-10 10:38:52 -07:00
c1be1f329f Prepare Update v5.2.0 Package Dependencies 2024-06-10 10:37:51 -07:00
c757cef549 Prepare Update v5.2.0 Package Dependencies 2024-06-10 10:37:21 -07:00
da9e4c026c Prepare Update v5.2.0 Package Dependencies 2024-06-10 10:37:04 -07:00
5821d67e69 Prepare Update v5.2.0 Package Dependencies 2024-06-10 10:36:47 -07:00
ed14f6d13f Prepare Update v5.2.0 Package Dependencies 2024-06-10 10:35:43 -07:00
35bdd8b4ef Prepare Update v5.2.0 Package Dependencies 2024-06-10 10:35:16 -07:00
9cf2d30e77 Prepare Update v5.2.0 2024-06-10 10:32:30 -07:00
a2140a3b7b Update v5.2.0 Package Dependencies 2024-06-10 10:30:37 -07:00
900d026bcb Update v5.2.0 Package Dependencies 2024-06-10 10:29:58 -07:00
68604ec15a Update Oqtane.Database.PostgreSQL.csproj Package Dependencies 2024-06-10 10:28:51 -07:00
f7c0ebb8d3 Update Oqtane.Database.MySQL.csproj prepare 5.2.0 2024-06-10 10:27:53 -07:00
8be5d0c72d Update Oqtane.Database.MySQL.csproj Package Dependencies 2024-06-10 10:27:15 -07:00
15c8b724e6 Update Oqtane.Client.csproj Package Dependencies 2024-06-10 10:24:52 -07:00
d6949200f9 Merge pull request #4323 from sbwalker/dev
move Search page/module to Admin template so that it is always provisioned
2024-06-10 12:33:31 -04:00
1c2abe794a move Search page/module to Admin template so that it is always provisioned 2024-06-10 12:33:09 -04:00
0bcf393586 Merge pull request #4320 from sbwalker/dev
search refactoring
2024-06-08 16:24:32 -04:00
bc0573918f search refactoring 2024-06-08 16:14:56 -04:00
175675ad99 Merge pull request #4317 from zyhfish/task/fix-issue-4316
Fix #4316: add text editor interfaces.
2024-06-07 14:44:40 -04:00
Ben
b00c2afc46 Fix #4316: move the raw html editor into quill editor instance. 2024-06-07 08:35:42 +08:00
Ben
c125a7fe07 Fix #4316: add text editor interfaces. 2024-06-06 16:39:35 +08:00
a42ab32436 Merge pull request #4310 from zyhfish/task/fix-searchbox-responsive-issue
Fix #4309: make searchbox responsive.
2024-06-05 07:48:55 -04:00
797a64976e Merge pull request #4311 from fonsecaf/fix-cookie-date-culture-format
Fix Cookie Date Conversion to Respect Culture and Format
2024-06-05 07:48:43 -04:00
ac377a8b68 Modified date parsing and formatting to use invariant culture, ensuring consistency and preventing non-ASCII characters in HTTP headers. 2024-06-05 13:39:31 +10:00
Ben
842b7b1402 Fix #4309: make searchbox responsive. 2024-06-05 10:27:38 +08:00
d449396ad5 Merge pull request #4304 from zyhfish/task/add-search-function
#4303: add search function.
2024-06-04 16:52:10 -04:00
532a87d064 Merge pull request #4307 from leigh-pointer/OqtaneControls
Oqtane controls updates
2024-06-04 16:50:53 -04:00
Ben
e1cdc7b387 return the words count to calculate the ranking. 2024-06-04 21:57:50 +08:00
Ben
d9d917e267 set search result page path to be parameter. 2024-06-04 21:09:25 +08:00
8048788042 Oqtane controls updates
ActionDialog and ActionLink now allow other icon sets whilst still adhering to the legacy "oi oi-" icon set.
Pager added SearchBoxClass Class parameter to the Search div.
TabStrip added a TabContentClass Class parameter to the tab content div.
2024-06-04 12:21:59 +02:00
Ben
790fc88e47 using correct module id value. 2024-06-04 17:50:29 +08:00
Ben
7f970d489f refactoring the code. 2024-06-04 17:32:31 +08:00
Ben
9d85ca07f4 #4303: add search function. 2024-06-03 21:19:42 +08:00
d75e3acdf3 Merge pull request #4302 from sbwalker/dev
changes as a result of #4299 related to  PageState.Modules
2024-06-03 07:42:46 -04:00
694cda0e99 changes as a result of #4299 related to PageState.Modules 2024-06-03 07:42:22 -04:00
94f134c6a7 Merge pull request #4301 from zyhfish/task/fix-add-page-issue
Fix Add Page Issue
2024-06-01 09:04:28 -04:00
Ben
83f329d93c Fix Add Page Issue 2024-06-01 09:08:43 +08:00
de49387fca Merge pull request #4300 from sbwalker/dev
remove LoadTestingSiteTemplate
2024-05-31 16:51:45 -04:00
e5567f2f46 remove LoadTestingSiteTemplate 2024-05-31 16:51:27 -04:00
80f545f3d5 Merge pull request #4299 from sbwalker/dev
scalability improvements
2024-05-31 16:23:50 -04:00
06f0cc70b8 scalability improvements 2024-05-31 16:23:36 -04:00
cf6b7544b0 Update README.md 2024-05-28 15:20:37 -04:00
d511c6334a Update README.md 2024-05-28 15:20:15 -04:00
0224fd6d54 Update README.md 2024-05-28 15:17:27 -04:00
2addcc3ab5 Merge branch 'dev' of https://github.com/Trifoia/oqtane.framework into dev 2024-03-28 13:47:43 -07:00
370b39a139 Merge branch 'release/v5.0.1' into dev 2024-01-27 21:18:58 -08:00
7b7e64576f Update appsettings.json 2024-01-27 20:30:20 -08:00
286928d59e Merge tag 'v5.0.1' into dev 2023-12-29 11:54:39 -08:00
cc65555c3d minor fix to routing 2023-12-05 12:17:02 -08:00
63e3923349 Merge remote-tracking branch 'oqtane/dev' into dev 2023-11-17 12:55:40 -08:00
357 changed files with 13871 additions and 5334 deletions

11
.gitignore vendored
View File

@ -22,10 +22,17 @@ Oqtane.Server/Packages
Oqtane.Server/wwwroot/Content
Oqtane.Server/wwwroot/Packages/*.log
Oqtane.Server/wwwroot/Modules
Oqtane.Server/wwwroot/_content/*
!Oqtane.Server/wwwroot/_content/Placeholder.txt
Oqtane.Server/wwwroot/Modules/*
!Oqtane.Server/wwwroot/Modules/Oqtane.Modules.*
!Oqtane.Server/wwwroot/Modules/Templates
Oqtane.Server/wwwroot/Modules/Templates/*
!Oqtane.Server/wwwroot/Modules/Templates/External
Oqtane.Server/wwwroot/Themes
Oqtane.Server/wwwroot/Themes/*
!Oqtane.Server/wwwroot/Themes/Oqtane.Themes.*
!Oqtane.Server/wwwroot/Themes/Templates
Oqtane.Server/wwwroot/Themes/Templates/*
Oqtane.Server/wwwroot/Themes/Templates/External

24
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,24 @@
# Contributing to Oqtane
## How to Contribute
We track all of our issues on Github. If you want to contribute, everything starts with an issue. If you don't have an issue yet, you can add one. Then a core contributor will tag it as either an enhancement [ENH] or a bug [BUG]. Tagged issues are open for contribution.
## Use GitHub-flow process
- Make a comment on the issue that you intend to work on it and read all the comments to gain a full understanding.
- Fork the repository
- Create a new branch and update your comment on the issue with a llink to the branch
- Make your changes and commit them
- Push to the branch
- Create a pull request
## Reporting Bugs
- Check if the issue has already been reported.
- Open a new issue if it hasnt been reported.
## Requesting Features
- Use the feature request template in the Issues tab.
Thank you for contributing!

View File

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2018-2024 .NET Foundation
Copyright (c) 2018-2025 .NET Foundation
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Components.Authorization;
using Oqtane.Interfaces;
using Oqtane.Providers;
using Oqtane.Services;
using Oqtane.Shared;
@ -50,6 +51,13 @@ namespace Microsoft.Extensions.DependencyInjection
services.AddScoped<IUrlMappingService, UrlMappingService>();
services.AddScoped<IVisitorService, VisitorService>();
services.AddScoped<ISyncService, SyncService>();
services.AddScoped<ILocalizationCookieService, LocalizationCookieService>();
services.AddScoped<ICookieConsentService, CookieConsentService>();
services.AddScoped<IOutputCacheService, OutputCacheService>();
// providers
services.AddScoped<ITextEditor, Oqtane.Modules.Controls.QuillJSTextEditor>();
services.AddScoped<ITextEditor, Oqtane.Modules.Controls.TextAreaTextEditor>();
return services;
}

View File

@ -15,7 +15,7 @@
<div class="row">
<div class="mx-auto text-center">
<img src="oqtane-black.png" />
<div style="font-weight: bold">@SharedLocalizer["Version"] @Constants.Version (.NET 8)</div>
<div style="font-weight: bold">@SharedLocalizer["Version"] @Constants.Version (.NET 9)</div>
</div>
</div>
<hr class="app-rule" />
@ -71,14 +71,14 @@
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="username" HelpText="Provide a username for the primary user account" ResourceKey="Username">Username:</Label>
<div class="col-sm-9">
<input id="username" type="text" class="form-control" @bind="@_hostUsername" />
<input id="username" type="text" class="form-control" maxlength="256" @bind="@_hostUsername" />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="password" HelpText="Provide a password for the primary user account" ResourceKey="Password">Password:</Label>
<div class="col-sm-9">
<div class="input-group">
<input id="password" type="@_passwordType" class="form-control" @bind="@_hostPassword" autocomplete="new-password" />
<input id="password" type="@_passwordType" class="form-control" maxlength="256" @bind="@_hostPassword" autocomplete="new-password" />
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword" tabindex="-1">@_togglePassword</button>
</div>
</div>
@ -87,7 +87,7 @@
<Label Class="col-sm-3" For="confirm" HelpText="Please confirm the password entered above by entering it again" ResourceKey="Confirm">Confirm:</Label>
<div class="col-sm-9">
<div class="input-group">
<input id="confirm" type="@_confirmPasswordType" class="form-control" @bind="@_confirmPassword" autocomplete="new-password" />
<input id="confirm" type="@_confirmPasswordType" class="form-control" maxlength="256" @bind="@_confirmPassword" autocomplete="new-password" />
<button type="button" class="btn btn-secondary" @onclick="@ToggleConfirmPassword" tabindex="-1">@_toggleConfirmPassword</button>
</div>
</div>
@ -95,7 +95,13 @@
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="email" HelpText="Provide the email address for the host user account" ResourceKey="Email">Email:</Label>
<div class="col-sm-9">
<input type="text" class="form-control" @bind="@_hostEmail" />
<input type="text" class="form-control" maxlength="256" @bind="@_hostEmail" />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="name" HelpText="Provide the full name of the host user" ResourceKey="Name">Full Name:</Label>
<div class="col-sm-9">
<input type="text" class="form-control" maxlength="50" @bind="@_hostName" />
</div>
</div>
<div class="row mb-1 align-items-center">
@ -153,132 +159,134 @@
private string _toggleConfirmPassword = string.Empty;
private string _confirmPassword = string.Empty;
private string _hostEmail = string.Empty;
private string _hostName = string.Empty;
private List<SiteTemplate> _templates;
private string _template = Constants.DefaultSiteTemplate;
private bool _register = true;
private string _message = string.Empty;
private string _loadingDisplay = "display: none;";
private string _message = string.Empty;
private string _loadingDisplay = "display: none;";
protected override async Task OnInitializedAsync()
{
protected override async Task OnInitializedAsync()
{
// include CSS
var content = "<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.0/css/bootstrap.min.css\" integrity=\"sha512-t4GWSVZO1eC8BM339Xd7Uphw5s17a86tIZIj8qRxhnKub6WoyhnrxeCIMeAqBPgdZGlCcG2PrZjMc+Wr78+5Xg==\" crossorigin=\"anonymous\" type=\"text/css\"/>";
var content = $"<link rel=\"stylesheet\" href=\"{Constants.BootstrapStylesheetUrl}\" integrity=\"{Constants.BootstrapStylesheetIntegrity}\" crossorigin=\"anonymous\" type=\"text/css\"/>";
SiteState.AppendHeadContent(content);
_togglePassword = SharedLocalizer["ShowPassword"];
_toggleConfirmPassword = SharedLocalizer["ShowPassword"];
_toggleConfirmPassword = SharedLocalizer["ShowPassword"];
_databases = await DatabaseService.GetDatabasesAsync();
if (_databases.Exists(item => item.IsDefault))
{
_databaseName = _databases.Find(item => item.IsDefault).Name;
}
else
{
_databaseName = "LocalDB";
}
LoadDatabaseConfigComponent();
_databases = await DatabaseService.GetDatabasesAsync();
if (_databases.Exists(item => item.IsDefault))
{
_databaseName = _databases.Find(item => item.IsDefault).Name;
}
else
{
_databaseName = "LocalDB";
}
LoadDatabaseConfigComponent();
_templates = await SiteTemplateService.GetSiteTemplatesAsync();
}
private void DatabaseChanged(ChangeEventArgs eventArgs)
{
try
{
_databaseName = (string)eventArgs.Value;
_showConnectionString = false;
LoadDatabaseConfigComponent();
}
catch
{
_message = Localizer["Error.DbConfig.Load"];
}
}
private void DatabaseChanged(ChangeEventArgs eventArgs)
{
try
{
_databaseName = (string)eventArgs.Value;
_showConnectionString = false;
LoadDatabaseConfigComponent();
}
catch
{
_message = Localizer["Error.DbConfig.Load"];
}
}
private void LoadDatabaseConfigComponent()
{
var database = _databases.SingleOrDefault(d => d.Name == _databaseName);
if (database != null)
{
_databaseConfigType = Type.GetType(database.ControlType);
DatabaseConfigComponent = builder =>
{
builder.OpenComponent(0, _databaseConfigType);
builder.AddComponentReferenceCapture(1, inst => { _databaseConfig = Convert.ChangeType(inst, _databaseConfigType); });
builder.CloseComponent();
};
}
}
private void LoadDatabaseConfigComponent()
{
var database = _databases.SingleOrDefault(d => d.Name == _databaseName);
if (database != null)
{
_databaseConfigType = Type.GetType(database.ControlType);
DatabaseConfigComponent = builder =>
{
builder.OpenComponent(0, _databaseConfigType);
builder.AddComponentReferenceCapture(1, inst => { _databaseConfig = Convert.ChangeType(inst, _databaseConfigType); });
builder.CloseComponent();
};
}
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
// include JavaScript
var interop = new Interop(JSRuntime);
await interop.IncludeScript("", "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.0/js/bootstrap.bundle.min.js", "sha512-VK2zcvntEufaimc+efOYi622VN5ZacdnufnmX7zIhCPmjhKnOi9ZDMtg1/ug5l183f19gG1/cBstPO4D8N/Img==", "anonymous", "", "head");
}
}
var interop = new Interop(JSRuntime);
await interop.IncludeScript("", Constants.BootstrapScriptUrl, Constants.BootstrapScriptIntegrity, "anonymous", "", "head");
}
}
private async Task Install()
{
var connectionString = String.Empty;
if (_showConnectionString)
{
connectionString = _connectionString;
}
else
{
if (_databaseConfig is IDatabaseConfigControl databaseConfigControl)
{
connectionString = databaseConfigControl.GetConnectionString();
}
}
private async Task Install()
{
var connectionString = String.Empty;
if (_showConnectionString)
{
connectionString = _connectionString;
}
else
{
if (_databaseConfig is IDatabaseConfigControl databaseConfigControl)
{
connectionString = databaseConfigControl.GetConnectionString();
}
}
if (connectionString != "" && !string.IsNullOrEmpty(_hostUsername) && !string.IsNullOrEmpty(_hostPassword) && _hostPassword == _confirmPassword && !string.IsNullOrEmpty(_hostEmail) && _hostEmail.Contains("@"))
{
if (await UserService.ValidatePasswordAsync(_hostPassword))
{
_loadingDisplay = "";
StateHasChanged();
if (connectionString != "" && !string.IsNullOrEmpty(_hostUsername) && !string.IsNullOrEmpty(_hostPassword) && _hostPassword == _confirmPassword && !string.IsNullOrEmpty(_hostEmail) && _hostEmail.Contains("@") && !string.IsNullOrEmpty(_hostName))
{
var result = await UserService.ValidateUserAsync(_hostUsername, _hostEmail, _hostPassword);
if (result.Succeeded)
{
_loadingDisplay = "";
StateHasChanged();
Uri uri = new Uri(NavigationManager.Uri);
Uri uri = new Uri(NavigationManager.Uri);
var database = _databases.SingleOrDefault(d => d.Name == _databaseName);
var database = _databases.SingleOrDefault(d => d.Name == _databaseName);
var config = new InstallConfig
{
DatabaseType = database.DBType,
ConnectionString = connectionString,
Aliases = uri.Authority,
HostUsername = _hostUsername,
HostPassword = _hostPassword,
HostEmail = _hostEmail,
HostName = _hostUsername,
TenantName = TenantNames.Master,
IsNewTenant = true,
SiteName = Constants.DefaultSite,
Register = _register,
SiteTemplate = _template,
RenderMode = RenderModes.Static,
Runtime = Runtimes.Server
};
var config = new InstallConfig
{
DatabaseType = database.DBType,
ConnectionString = connectionString,
Aliases = uri.Authority,
HostUsername = _hostUsername,
HostPassword = _hostPassword,
HostEmail = _hostEmail,
HostName = _hostName,
TenantName = TenantNames.Master,
IsNewTenant = true,
SiteName = Constants.DefaultSite,
Register = _register,
SiteTemplate = _template,
RenderMode = RenderModes.Static,
Runtime = Runtimes.Server
};
var installation = await InstallationService.Install(config);
if (installation.Success)
{
NavigationManager.NavigateTo(uri.Scheme + "://" + uri.Authority, true);
}
else
{
_message = installation.Message;
_loadingDisplay = "display: none;";
}
}
else
{
_message = Localizer["Message.Password.Invalid"];
var installation = await InstallationService.Install(config);
if (installation.Success)
{
NavigationManager.NavigateTo(uri.Scheme + "://" + uri.Authority, true);
}
else
{
_message = installation.Message;
_loadingDisplay = "display: none;";
}
}
else
{
_message = string.Join("<br />", result.Errors.Select(i => !string.IsNullOrEmpty(i.Value) ? i.Value : Localizer[i.Key]));
}
}
else

View File

@ -40,10 +40,14 @@
</div>
</div>
</div>
<br />
<button type="button" class="btn btn-success" @onclick="SaveFile">@SharedLocalizer["Save"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
<br />
<br />
@if (_name.ToLower().EndsWith(".zip"))
{
<button type="button" class="btn btn-primary mx-1" @onclick="UnzipFile">Unzip</button>
}
<br /><br />
<AuditInfo CreatedBy="@_createdBy" CreatedOn="@_createdOn" ModifiedBy="@_modifiedBy" ModifiedOn="@_modifiedOn"></AuditInfo>
</form>
}
@ -126,4 +130,18 @@
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
}
}
private async Task UnzipFile()
{
try
{
await FileService.UnzipFileAsync(_fileId);
NavigationManager.NavigateTo(NavigateUrl());
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Unzipping File {FileId} {Error}", _fileId, ex.Message);
AddModuleMessage(Localizer["Error.File.Unzip"], MessageType.Error);
}
}
}

View File

@ -8,67 +8,83 @@
@if (_folders != null)
{
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
<div class="container">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="parent" HelpText="Select the parent folder" ResourceKey="Parent">Parent: </Label>
<div class="col-sm-9">
<select id="parent" class="form-select" @bind="@_parentId" required>
@if (PageState.QueryString.ContainsKey("id"))
{
<option value="-1">&lt;@Localizer["NoParent"]&gt;</option>
}
@foreach (Folder folder in _folders)
{
<option value="@(folder.FolderId)">@(new string('-', folder.Level * 2))@(folder.Name)</option>
}
</select>
<TabStrip>
<TabPanel Name="Settings" ResourceKey="Settings" Heading="Settings">
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
<div class="container">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="parent" HelpText="Select the parent folder" ResourceKey="Parent">Parent: </Label>
<div class="col-sm-9">
<select id="parent" class="form-select" @bind="@_parentId" required>
@if (PageState.QueryString.ContainsKey("id"))
{
<option value="-1">&lt;@Localizer["NoParent"]&gt;</option>
}
@foreach (Folder folder in _folders)
{
<option value="@(folder.FolderId)">@(new string('-', folder.Level * 2))@(folder.Name)</option>
}
</select>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="name" HelpText="Enter the folder name" ResourceKey="Name">Name: </Label>
<div class="col-sm-9">
<input id="name" class="form-control" @bind="@_name" maxlength="256" required />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="type" HelpText="Select the folder type. Private folders are only accessible by authorized users. Public folders can be accessed by all users" ResourceKey="Type">Type: </Label>
<div class="col-sm-9">
@if (PageState.QueryString.ContainsKey("id"))
{
<input id="type" class="form-control" readonly @bind="@_type" />
}
else
{
<select id="type" class="form-select" @bind="@_type" required>
<option value="@FolderTypes.Private">@Localizer[FolderTypes.Private]</option>
<option value="@FolderTypes.Public">@Localizer[FolderTypes.Public]</option>
</select>
}
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="capacity" HelpText="Enter the maximum folder capacity (in megabytes). Specify zero if the capacity is unlimited." ResourceKey="Capacity">Capacity: </Label>
<div class="col-sm-9">
<input id="capacity" class="form-control" @bind="@_capacity" required />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="cachecontrol" HelpText="Optionally provide a Cache-Control directive for this folder. For example 'public, max-age=60' indicates that files in this folder should be cached for 60 seconds. Please note that when caching is enabled, changes to files will not be immediately reflected in the UI." ResourceKey="CacheControl">Caching: </Label>
<div class="col-sm-9">
<input id="cachecontrol" class="form-control" @bind="@_cachecontrol" maxlength="50" />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="imagesizes" HelpText="Optionally enter a list of image sizes which can be generated dynamically from uploaded images (ie. 200x200,400x400). Use * to indicate the folder supports all image sizes (not recommended)." ResourceKey="ImageSizes">Image Sizes: </Label>
<div class="col-sm-9">
<input id="imagesizes" class="form-control" @bind="@_imagesizes" maxlength="512" />
</div>
</div>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="name" HelpText="Enter the folder name" ResourceKey="Name">Name: </Label>
<div class="col-sm-9">
<input id="name" class="form-control" @bind="@_name" maxlength="256" required />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="type" HelpText="Select the folder type. Private folders are only accessible by authorized users. Public folders can be accessed by all users" ResourceKey="Type">Type: </Label>
<div class="col-sm-9">
@if (PageState.QueryString.ContainsKey("id"))
{
<input id="type" class="form-control" readonly @bind="@_type" />
}
else
{
<select id="type" class="form-select" @bind="@_type" required>
<option value="@FolderTypes.Private">@Localizer[FolderTypes.Private]</option>
<option value="@FolderTypes.Public">@Localizer[FolderTypes.Public]</option>
</select>
}
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="imagesizes" HelpText="Enter a list of image sizes which can be generated dynamically from uploaded images (ie. 200x200,400x400). Use * to indicate the folder supports all image sizes." ResourceKey="ImageSizes">Image Sizes: </Label>
<div class="col-sm-9">
<input id="imagesizes" class="form-control" @bind="@_imagesizes" maxlength="512" />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="capacity" HelpText="Enter the maximum folder capacity (in megabytes). Specify zero if the capacity is unlimited." ResourceKey="Capacity">Capacity: </Label>
<div class="col-sm-9">
<input id="capacity" class="form-control" @bind="@_capacity" required />
</div>
</div>
<div class="row mb-1 align-items-center">
<div class="col-sm-12">
<Label Class="col-sm-3" For="permissions" HelpText="Select the permissions you want for the folder" ResourceKey="Permissions">Permissions: </Label>
@if (PageState.QueryString.ContainsKey("id"))
{
<br />
<AuditInfo CreatedBy="@_createdBy" CreatedOn="@_createdOn" ModifiedBy="@_modifiedBy" ModifiedOn="@_modifiedOn"></AuditInfo>
}
</form>
</TabPanel>
<TabPanel Name="Permissions" ResourceKey="Permissions" Heading="Permissions">
<div class="container">
<div class="row mb-1 align-items-center">
<PermissionGrid EntityName="@EntityNames.Folder" PermissionNames="@(PermissionNames.Browse + "," + PermissionNames.View + "," + PermissionNames.Edit)" PermissionList="@_permissions" @ref="_permissionGrid" />
</div>
</div>
</div>
</form>
<br /><br />
</TabPanel>
</TabStrip>
<br />
@if (!_isSystem)
{
<button type="button" class="btn btn-success" @onclick="SaveFolder">@SharedLocalizer["Save"]</button>
@ -80,11 +96,6 @@
@((MarkupString)"&nbsp;")
<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 />
@if (PageState.QueryString.ContainsKey("id"))
{
<AuditInfo CreatedBy="@_createdBy" CreatedOn="@_createdOn" ModifiedBy="@_modifiedBy" ModifiedOn="@_modifiedOn"></AuditInfo>
}
}
@code {
@ -95,8 +106,9 @@
private int _parentId = -1;
private string _name;
private string _type = FolderTypes.Private;
private string _imagesizes = string.Empty;
private string _capacity = "0";
private string _cachecontrol = string.Empty;
private string _imagesizes = string.Empty;
private bool _isSystem;
private List<Permission> _permissions = null;
private string _createdBy;
@ -127,8 +139,9 @@
_parentId = folder.ParentId ?? -1;
_name = folder.Name;
_type = folder.Type;
_imagesizes = folder.ImageSizes;
_capacity = folder.Capacity.ToString();
_cachecontrol = folder.CacheControl;
_imagesizes = folder.ImageSizes;
_isSystem = folder.IsSystem;
_permissions = folder.PermissionList;
_createdBy = folder.CreatedBy;
@ -188,7 +201,7 @@
{
folder.ParentId = _parentId;
}
// check for duplicate folder names
if (_folders.Any(item => item.ParentId == folder.ParentId && item.Name == _name && item.FolderId != _folderId))
{
@ -199,8 +212,9 @@
folder.SiteId = PageState.Site.SiteId;
folder.Name = _name;
folder.Type = _type;
folder.ImageSizes = _imagesizes;
folder.Capacity = int.Parse(_capacity);
folder.CacheControl = _cachecontrol;
folder.ImageSizes = _imagesizes;
folder.IsSystem = _isSystem;
folder.PermissionList = _permissionGrid.GetPermissionList();

View File

@ -3,54 +3,92 @@
@inject NavigationManager NavigationManager
@inject IFolderService FolderService
@inject IFileService FileService
@inject ISettingService SettingService
@inject IStringLocalizer<Index> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer
@if (_files != null)
@if (_files == null)
{
<div class="row">
<div class="col-md mb-1">
<ActionLink Action="Edit" Text="Add Folder" Class="btn btn-secondary" ResourceKey="AddFolder" />
</div>
<div class="col-md-8 mb-1">
<div class="input-group">
<span class="input-group-text">@Localizer["Folder"]:</span>
<select class="form-select" @onchange="(e => FolderChanged(e))">
@foreach (Folder folder in _folders)
{
<option value="@(folder.FolderId)">@(new string('-', folder.Level * 2))@(folder.Name)</option>
}
</select>
<ActionLink Action="Edit" Text="Edit Folder" Class="btn btn-secondary" Parameters="@($"id=" + _folderId.ToString())" ResourceKey="EditFolder" />&nbsp;
<p>
<em>@SharedLocalizer["Loading"]</em>
</p>
}
else
{
<TabStrip>
<TabPanel Name="Files" Heading="Files" ResourceKey="Files">
<div class="row">
<div class="col-md mb-1">
<ActionLink Action="Edit" Text="Add Folder" Class="btn btn-secondary" ResourceKey="AddFolder" />
</div>
<div class="col-md-8 mb-1">
<div class="input-group">
<span class="input-group-text">@Localizer["Folder"]:</span>
<select class="form-select" @onchange="(e => FolderChanged(e))">
@foreach (Folder folder in _folders)
{
<option value="@(folder.FolderId)">@(new string('-', folder.Level * 2))@(folder.Name)</option>
}
</select>
<ActionLink Action="Edit" Text="Edit Folder" Class="btn btn-secondary" Parameters="@($"id=" + _folderId.ToString())" ResourceKey="EditFolder" />&nbsp;
</div>
</div>
<div class="col-md mb-1 text-end">
<ActionLink Action="Add" Text="Upload Files" Parameters="@($"id=" + _folderId.ToString())" ResourceKey="UploadFiles" />
</div>
</div>
</div>
<div class="col-md mb-1 text-end">
<ActionLink Action="Add" Text="Upload Files" Parameters="@($"id=" + _folderId.ToString())" ResourceKey="UploadFiles" />
</div>
</div>
<Pager Items="@_files" SearchProperties="Name">
<Header>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>@SharedLocalizer["Name"]</th>
<th>@Localizer["Modified"]</th>
<th>@Localizer["Type"]</th>
<th>@Localizer["Size"]</th>
</Header>
<Row>
<td><ActionLink Action="Details" Text="Edit" Parameters="@($"id=" + context.FileId.ToString())" ResourceKey="Details" /></td>
<td><ActionDialog Header="Delete File" Message="@string.Format(Localizer["Confirm.File.Delete"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteFile(context))" ResourceKey="DeleteFile" /></td>
<td><a href="@context.Url" target="_new">@context.Name</a></td>
<td>@context.ModifiedOn</td>
<td>@context.Extension.ToUpper() @SharedLocalizer["File"]</td>
<td>@string.Format("{0:0.00}", ((decimal)context.Size / 1000)) KB</td>
</Row>
</Pager>
@if (_files.Count == 0)
{
<div class="text-center">@Localizer["NoFiles"]</div>
}
@if (_files.Count != 0)
{
<Pager Items="@_files" SearchProperties="Name">
<Header>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>@SharedLocalizer["Name"]</th>
<th>@Localizer["Modified"]</th>
<th>@Localizer["Type"]</th>
<th>@Localizer["Size"]</th>
</Header>
<Row>
<td><ActionLink Action="Details" Text="Edit" Parameters="@($"id=" + context.FileId.ToString())" ResourceKey="Details" /></td>
<td><ActionDialog Header="Delete File" Message="@string.Format(Localizer["Confirm.File.Delete"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteFile(context))" ResourceKey="DeleteFile" /></td>
<td><a href="@context.Url" target="_new">@context.Name</a></td>
<td>@context.ModifiedOn</td>
<td>@context.Extension.ToUpper() @SharedLocalizer["File"]</td>
<td>@string.Format("{0:0.00}", ((decimal)context.Size / 1000)) KB</td>
</Row>
</Pager>
}
else
{
<div class="text-center">@Localizer["NoFiles"]</div>
}
</TabPanel>
<TabPanel Name="Settings" Heading="Settings" ResourceKey="Settings" Security="SecurityAccessLevel.Admin">
<div class="container">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="imageExt" HelpText="Enter a comma separated list of image file extensions" ResourceKey="ImageExtensions">Image Extensions: </Label>
<div class="col-sm-9">
<input id="imageExt" spellcheck="false" class="form-control" @bind="@_imageFiles" />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="uploadableFileExt" HelpText="Enter a comma separated list of uploadable file extensions" ResourceKey="UploadableFileExtensions">Uploadable File Extensions: </Label>
<div class="col-sm-9">
<input id="uploadableFileExt" spellcheck="false" class="form-control" @bind="@_uploadableFiles" />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="maxChunkSize" HelpText="Files are split into chunks to streamline the upload process. Specify the maximum chunk size in MB (note that higher chunk sizes should only be used on faster networks)." ResourceKey="MaxChunkSize">Max Upload Chunk Size (MB): </Label>
<div class="col-sm-9">
<input id="maxChunkSize" type="number" min="1" max="10" step="1" class="form-control" @bind="@_maxChunkSize" />
</div>
</div>
</div>
<br />
<button type="button" class="btn btn-success" @onclick="SaveSiteSettings">@SharedLocalizer["Save"]</button>
</TabPanel>
</TabStrip>
}
@code {
@ -58,6 +96,10 @@
private int _folderId = -1;
private List<File> _files;
private string _imageFiles = string.Empty;
private string _uploadableFiles = string.Empty;
private int _maxChunkSize = 1;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
protected override async Task OnParametersSetAsync()
@ -71,6 +113,13 @@
_folderId = _folders[0].FolderId;
await GetFiles();
}
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
_imageFiles = SettingService.GetSetting(settings, "ImageFiles", Constants.ImageFiles);
_imageFiles = (string.IsNullOrEmpty(_imageFiles)) ? Constants.ImageFiles : _imageFiles;
_uploadableFiles = SettingService.GetSetting(settings, "UploadableFiles", Constants.UploadableFiles);
_uploadableFiles = (string.IsNullOrEmpty(_uploadableFiles)) ? Constants.UploadableFiles : _uploadableFiles;
_maxChunkSize = int.Parse(SettingService.GetSetting(settings, "MaxChunkSize", "1"));
}
catch (Exception ex)
{
@ -115,4 +164,23 @@
AddModuleMessage(string.Format(Localizer["Error.File.Delete"], file.Name), MessageType.Error);
}
}
private async Task SaveSiteSettings()
{
try
{
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
settings = SettingService.SetSetting(settings, "ImageFiles", (_imageFiles != Constants.ImageFiles) ? _imageFiles.Replace(" ", "") : "", false);
settings = SettingService.SetSetting(settings, "UploadableFiles", (_uploadableFiles != Constants.UploadableFiles) ? _uploadableFiles.Replace(" ", "") : "", false);
settings = SettingService.SetSetting(settings, "MaxChunkSize", _maxChunkSize.ToString(), false);
await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId);
AddModuleMessage(Localizer["Success.SaveSiteSettings"], MessageType.Success);
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Saving Site Settings {Error}", ex.Message);
AddModuleMessage(Localizer["Error.SaveSiteSettings"], MessageType.Error);
}
}
}

View File

@ -0,0 +1,19 @@
using Oqtane.Documentation;
using Oqtane.Models;
using Oqtane.Shared;
namespace Oqtane.Modules.Admin.Files
{
[PrivateApi("Mark this as private, since it's not very useful in the public docs")]
public class ModuleInfo : IModule
{
public ModuleDefinition ModuleDefinition => new ModuleDefinition
{
Name = "File Management",
Description = "File Management",
Version = Constants.Version,
Categories = "Admin",
ServerManagerType = "Oqtane.Modules.Admin.Files.Manager.FileManager, Oqtane.Server"
};
}
}

View File

@ -10,9 +10,7 @@
}
else
{
<ActionLink Action="Log" Class="btn btn-secondary" Text="View Logs" ResourceKey="ViewJobs" />
<button type="button" class="btn btn-secondary" @onclick="(async () => await Refresh())">@Localizer["Refresh.Text"]</button>
<br />
<button type="button" class="btn btn-secondary" @onclick="Refresh">@Localizer["Refresh.Text"]</button>
<br />
<Pager Items="@_jobs" SearchProperties="Name">
@ -44,21 +42,25 @@ else
</td>
</Row>
</Pager>
<br />
<ActionLink Action="Log" Class="btn btn-secondary" Text="View All Logs" ResourceKey="ViewLogs" />
}
@code {
private List<Job> _jobs;
public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } }
public override SecurityAccessLevel SecurityAccessLevel { get { return SecurityAccessLevel.Host; } }
protected override async Task OnInitializedAsync()
{
await GetJobs();
}
private async Task GetJobs()
{
_jobs = await JobService.GetJobsAsync();
if (_jobs.Count == 0)
{
AddModuleMessage(string.Format(Localizer["Message.NoJobs"], NavigateUrl("admin/system")), MessageType.Warning);
}
}
}
private string DisplayStatus(bool isEnabled, bool isExecuting)
{
@ -146,7 +148,8 @@ else
private async Task Refresh()
{
_jobs = await JobService.GetJobsAsync();
StateHasChanged();
ShowProgressIndicator();
await GetJobs();
HideProgressIndicator();
}
}

View File

@ -10,6 +10,9 @@
}
else
{
<button type="button" class="btn btn-secondary" @onclick="Refresh">@Localizer["Refresh"]</button>
<br /><br />
<Pager Items="@_jobLogs">
<Header>
<th>@SharedLocalizer["Name"]</th>
@ -35,6 +38,11 @@ else
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
protected override async Task OnParametersSetAsync()
{
await GetJobLogs();
}
private async Task GetJobLogs()
{
_jobLogs = await JobLogService.GetJobLogsAsync();
@ -67,4 +75,11 @@ else
return status;
}
private async Task Refresh()
{
ShowProgressIndicator();
await GetJobLogs();
HideProgressIndicator();
}
}

View File

@ -1,7 +1,6 @@
@namespace Oqtane.Modules.Admin.Languages
@inherits ModuleBase
@using System.Globalization
@using Microsoft.AspNetCore.Localization
@inject NavigationManager NavigationManager
@inject ILocalizationService LocalizationService
@inject ILanguageService LanguageService
@ -94,7 +93,6 @@ else
var language = new Language
{
SiteId = PageState.Page.SiteId,
Name = CultureInfo.GetCultureInfo(_code).DisplayName,
Code = _code,
IsDefault = (_default == null ? false : Boolean.Parse(_default))
};
@ -130,7 +128,7 @@ else
{
var interop = new Interop(JSRuntime);
var localizationCookieValue = CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture));
await interop.SetCookie(CookieRequestCultureProvider.DefaultCookieName, localizationCookieValue, 360);
await interop.SetCookie(CookieRequestCultureProvider.DefaultCookieName, localizationCookieValue, 360, true, "Lax");
}
}

View File

@ -1,7 +1,6 @@
@namespace Oqtane.Modules.Admin.Languages
@inherits ModuleBase
@using System.Globalization
@using Microsoft.AspNetCore.Localization
@inject NavigationManager NavigationManager
@inject ILocalizationService LocalizationService
@inject ILanguageService LanguageService
@ -103,7 +102,7 @@ else
{
var interop = new Interop(JSRuntime);
var localizationCookieValue = CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture));
await interop.SetCookie(CookieRequestCultureProvider.DefaultCookieName, localizationCookieValue, 360);
await interop.SetCookie(CookieRequestCultureProvider.DefaultCookieName, localizationCookieValue, 360, true, "Lax");
}
}

View File

@ -8,74 +8,77 @@
@inject IStringLocalizer<Index> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer
<AuthorizeView Roles="@RoleNames.Registered">
<Authorizing>
<text>...</text>
</Authorizing>
<Authorized>
<ModuleMessage Message="@Localizer["Info.SignedIn"]" Type="MessageType.Info" />
</Authorized>
<NotAuthorized>
@if (!twofactor)
{
<form @ref="login" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
<div class="Oqtane-Modules-Admin-Login" @onkeypress="@(e => KeyPressed(e))">
@if (_allowexternallogin)
{
<button type="button" class="btn btn-primary" @onclick="ExternalLogin">@Localizer["Use"] @PageState.Site.Settings["ExternalLogin:ProviderName"]</button>
<br /><br />
}
@if (_allowsitelogin)
{
<div class="form-group">
<Label Class="control-label" For="username" HelpText="Please enter your Username" ResourceKey="Username">Username:</Label>
<input id="username" type="text" @ref="username" class="form-control" placeholder="@Localizer["Username.Placeholder"]" @bind="@_username" required />
</div>
<div class="form-group mt-2">
<Label Class="control-label" For="password" HelpText="Please enter your Password" ResourceKey="Password">Password:</Label>
<div class="input-group">
<input id="password" type="@_passwordtype" name="Password" class="form-control" placeholder="@Localizer["Password.Placeholder"]" @bind="@_password" required />
@if (PageState.User != null)
{
<ModuleMessage Message="@Localizer["Info.SignedIn"]" Type="MessageType.Info" />
}
else
{
@if (!twofactor)
{
<form @ref="login" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
<div class="Oqtane-Modules-Admin-Login" @onkeypress="@(e => KeyPressed(e))">
@if (_allowexternallogin)
{
<button type="button" class="btn btn-primary" @onclick="ExternalLogin">@Localizer["Use"] @PageState.Site.Settings["ExternalLogin:ProviderName"]</button>
<br />
<br />
}
@if (_allowsitelogin)
{
<div class="form-group">
<Label Class="control-label" For="username" HelpText="Please enter your Username" ResourceKey="Username">Username:</Label>
<input id="username" type="text" @ref="username" class="form-control" placeholder="@Localizer["Username.Placeholder"]" @bind="@_username" @bind:event="oninput" required />
</div>
<div class="form-group mt-2">
<Label Class="control-label" For="password" HelpText="Please enter your Password" ResourceKey="Password">Password:</Label>
<div class="input-group">
<input id="password" type="@_passwordtype" name="Password" class="form-control" placeholder="@Localizer["Password.Placeholder"]" @bind="@_password" @bind:event="oninput" required />
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword" tabindex="-1">@_togglepassword</button>
</div>
</div>
<div class="form-group mt-2">
@if (!_alwaysremember)
{
<div class="form-check">
<input id="remember" type="checkbox" class="form-check-input" @bind="@_remember" />
<Label Class="control-label" For="remember" HelpText="Specify if you would like to be signed back in automatically the next time you visit this site" ResourceKey="Remember">Remember Me?</Label>
</div>
}
</div>
<button type="button" class="btn btn-primary" @onclick="Login">@SharedLocalizer["Login"]</button>
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
<br /><br />
<button type="button" class="btn btn-secondary" @onclick="Forgot">@Localizer["ForgotPassword"]</button>
@if (PageState.Site.AllowRegistration)
</div>
</div>
<div class="form-group mt-2">
@if (!_alwaysremember)
{
<br /><br />
<NavLink href="@NavigateUrl("register")">@Localizer["Register"]</NavLink>
<div class="form-check">
<input id="remember" type="checkbox" class="form-check-input" @bind="@_remember" />
<Label Class="control-label" For="remember" HelpText="Specify if you would like to be signed back in automatically the next time you visit this site" ResourceKey="Remember">Remember Me?</Label>
</div>
}
</div>
<button type="button" class="btn btn-primary" @onclick="Login">@SharedLocalizer["Login"]</button>
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
<br />
<br />
<button type="button" class="btn btn-secondary" @onclick="Forgot">@Localizer["ForgotPassword"]</button>
@if (PageState.Site.AllowRegistration)
{
<br />
<br />
<NavLink href="@NavigateUrl("register")">@Localizer["Register"]</NavLink>
}
</div>
</form>
}
else
{
<form @ref="login" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
<div class="container Oqtane-Modules-Admin-Login">
<div class="form-group">
<Label Class="control-label" For="code" HelpText="Please enter the secure verification code which was sent to you by email" ResourceKey="Code">Verification Code:</Label>
<input id="code" class="form-control" @bind="@_code" placeholder="@Localizer["Code.Placeholder"]" maxlength="6" required />
</div>
<br />
<button type="button" class="btn btn-primary" @onclick="Login">@SharedLocalizer["Login"]</button>
<button type="button" class="btn btn-secondary" @onclick="Reset">@SharedLocalizer["Cancel"]</button>
</div>
</form>
}
</NotAuthorized>
</AuthorizeView>
}
</div>
</form>
}
else
{
<form @ref="login" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
<div class="container Oqtane-Modules-Admin-Login">
<div class="form-group">
<Label Class="control-label" For="code" HelpText="Please enter the secure verification code which was sent to you by email" ResourceKey="Code">Verification Code:</Label>
<input id="code" class="form-control" @bind="@_code" placeholder="@Localizer["Code.Placeholder"]" maxlength="6" required />
</div>
<br />
<button type="button" class="btn btn-primary" @onclick="Login">@SharedLocalizer["Login"]</button>
<button type="button" class="btn btn-secondary" @onclick="Reset">@SharedLocalizer["Cancel"]</button>
</div>
</form>
}
}
@code {
private bool _allowsitelogin = true;
@ -93,6 +96,7 @@
private string _code = string.Empty;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Anonymous;
public override bool? Prerender => true;
public override List<Resource> Resources => new List<Resource>()
{
@ -203,28 +207,31 @@
user = await UserService.VerifyTwoFactorAsync(user, _code);
}
if (user.IsAuthenticated)
if (user != null && user.IsAuthenticated)
{
await logger.LogInformation(LogFunction.Security, "Login Successful For Username {Username}", _username);
await logger.LogInformation(LogFunction.Security, "Login Successful For {Username} From IP Address {IPAddress}", _username, SiteState.RemoteIPAddress);
// return url is not specified if user navigated directly to login page
var returnurl = (!string.IsNullOrEmpty(PageState.ReturnUrl)) ? PageState.ReturnUrl : PageState.Alias.Path;
if (hybrid)
{
// hybrid apps utilize an interactive login
var authstateprovider = (IdentityAuthenticationStateProvider)ServiceProvider.GetService(typeof(IdentityAuthenticationStateProvider));
authstateprovider.NotifyAuthenticationChanged();
NavigationManager.NavigateTo(NavigateUrl(PageState.ReturnUrl, true));
NavigationManager.NavigateTo(NavigateUrl(returnurl, true));
}
else
{
// post back to the Login page so that the cookies are set correctly
var fields = new { __RequestVerificationToken = SiteState.AntiForgeryToken, username = _username, password = _password, remember = _remember, returnurl = WebUtility.UrlEncode(PageState.ReturnUrl) };
var fields = new { __RequestVerificationToken = SiteState.AntiForgeryToken, username = _username, password = _password, remember = _remember, returnurl = WebUtility.UrlEncode(returnurl) };
string url = Utilities.TenantUrl(PageState.Alias, "/pages/login/");
await interop.SubmitForm(url, fields);
}
}
else
{
if (SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:TwoFactor", "false") == "required" || user.TwoFactorRequired)
if (SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:TwoFactor", "false") == "required" || (user != null && user.TwoFactorRequired))
{
twofactor = true;
validated = false;
@ -235,12 +242,12 @@
if (!twofactor)
{
await logger.LogInformation(LogFunction.Security, "Login Failed For Username {Username}", _username);
AddModuleMessage(Localizer["Error.Login.Fail"], MessageType.Error);
AddModuleMessage(Localizer["Error.Login.Fail"], MessageType.Error);
}
else
{
await logger.LogInformation(LogFunction.Security, "Two Factor Verification Failed For Username {Username}", _username);
AddModuleMessage(Localizer["Error.TwoFactor.Fail"], MessageType.Error);
AddModuleMessage(Localizer["Error.TwoFactor.Fail"], MessageType.Error);
}
}
}

View File

@ -125,7 +125,7 @@
<TabPanel Name="Upload" ResourceKey="Upload" Heading="Upload">
<div class="container">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" HelpText="Upload one or more module packages. Once they are uploaded click Install to complete the installation." ResourceKey="Module">Module: </Label>
<Label Class="col-sm-3" HelpText="Upload one or more module packages." ResourceKey="Module">Module: </Label>
<div class="col-sm-9">
<FileManager Folder="@Constants.PackagesFolder" UploadMultiple="true" OnUpload="OnUpload" />
</div>

View File

@ -27,7 +27,7 @@
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="description" HelpText="Enter a short description for the module" ResourceKey="Description">Description: </Label>
<div class="col-sm-9">
<textarea id="description" class="form-control" @bind="@_description" rows="3" maxlength="2000" required></textarea>
<textarea id="description" class="form-control" @bind="@_description" rows="3" maxlength="2000"></textarea>
</div>
</div>
<div class="row mb-1 align-items-center">
@ -111,6 +111,8 @@
private async Task CreateModule()
{
validated = true;
_owner = _owner.Trim();
_module = _module.Trim();
var interop = new Interop(JSRuntime);
if (await interop.FormValid(form))
{
@ -118,6 +120,7 @@
{
if (IsValid(_owner) && IsValid(_module) && _owner != _module && _template != "-")
{
if (string.IsNullOrEmpty(_description)) _description = _module;
if (IsValidXML(_description))
{
var template = _templates.FirstOrDefault(item => item.Name == _template);

View File

@ -1,7 +1,6 @@
@namespace Oqtane.Modules.Admin.ModuleDefinitions
@inherits ModuleBase
@using System.Globalization
@using Microsoft.AspNetCore.Localization
@inject IModuleDefinitionService ModuleDefinitionService
@inject IPackageService PackageService
@inject ILanguageService LanguageService
@ -10,6 +9,7 @@
@inject IStringLocalizer<SharedResources> SharedLocalizer
@inject IPageModuleService PageModuleService
@inject IModuleService ModuleService
@inject IPageService PageService
@if (_initialized)
{
@ -63,24 +63,7 @@
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="packagename" HelpText="The unique name of the package from which this module was installed. This value must be specified within the module's IModule interface specification." ResourceKey="PackageName">Package Name: </Label>
<div class="col-sm-9">
@if (!string.IsNullOrEmpty(_packagename))
{
<div class="input-group">
<input id="packagename" class="form-control" @bind="@_packagename" disabled />
@if (string.IsNullOrEmpty(_packageurl))
{
<button type="button" class="btn btn-secondary" @onclick="ValidatePackage">@Localizer["Validate"]</button>
}
else
{
<a href="@_packageurl" target="_blank" class="btn btn-primary">@SharedLocalizer["Download"]</a>
}
</div>
}
else
{
<input id="packagename" class="form-control" @bind="@_packagename" disabled />
}
<input id="packagename" class="form-control" @bind="@_packagename" disabled />
</div>
</div>
<div class="row mb-1 align-items-center">
@ -244,7 +227,6 @@
private string _moduledefinitionname = "";
private string _version;
private string _packagename = "";
private string _packageurl = "";
private string _owner = "";
private string _url = "";
private string _contact = "";
@ -307,13 +289,15 @@
}
// get distinct pages where module exists
var distinctPageIds = PageState.Modules
var modules = await ModuleService.GetModulesAsync(PageState.Site.SiteId);
var distinctPageIds = modules
.Where(md => md.ModuleDefinition?.ModuleDefinitionId == _moduleDefinitionId && md.IsDeleted == false)
.Select(md => md.PageId)
.Distinct();
// Filter and retrieve the corresponding pages
_pagesWithModules = PageState.Pages
// retrieve the pages which contain the module
var pages = await PageService.GetPagesAsync(PageState.Site.SiteId);
_pagesWithModules = pages
.Where(pg => distinctPageIds.Contains(pg.PageId) && pg.IsDeleted == false)
.ToList();
@ -443,27 +427,5 @@
}
}
private async Task ValidatePackage()
{
try
{
var package = await PackageService.GetPackageAsync(_packagename, _version, true);
if (package == null || string.IsNullOrEmpty(package.PackageUrl))
{
AddModuleMessage(Localizer["Message.Validate"], MessageType.Warning);
}
else
{
_packageurl = package.PackageUrl;
AddModuleMessage(Localizer["Message.Download"], MessageType.Info);
}
StateHasChanged();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Downloading Package {PackageId} {Version}", _packagename, _version);
AddModuleMessage(Localizer["Error.Validate"], MessageType.Error);
}
}
private string Browse(Page page) => string.IsNullOrEmpty(page.Url) ? NavigateUrl(page.Path) : page.Url;
}

View File

@ -1,6 +1,7 @@
@namespace Oqtane.Modules.Admin.ModuleDefinitions
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IModuleService ModuleService
@inject IModuleDefinitionService ModuleDefinitionService
@inject IPackageService PackageService
@inject IStringLocalizer<Index> Localizer
@ -70,7 +71,7 @@ else
}
</td>
<td>
@if (context.AssemblyName == Constants.ClientId || PageState.Modules.Where(m => m.ModuleDefinition?.ModuleDefinitionId == context.ModuleDefinitionId).FirstOrDefault() != null)
@if (context.AssemblyName == Constants.ClientId || _modules.Where(m => m.ModuleDefinition?.ModuleDefinitionId == context.ModuleDefinitionId).FirstOrDefault() != null)
{
<span>@SharedLocalizer["Yes"]</span>
}
@ -99,6 +100,7 @@ else
}
@code {
private List<Module> _modules;
private List<ModuleDefinition> _allModuleDefinitions;
private List<ModuleDefinition> _moduleDefinitions;
private List<Package> _packages;
@ -111,6 +113,7 @@ else
{
try
{
_modules = await ModuleService.GetModulesAsync(PageState.Site.SiteId);
_allModuleDefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync(PageState.Site.SiteId);
_categories = _allModuleDefinitions.SelectMany(m => m.Categories.Split(',')).Distinct().ToList();
await LoadModuleDefinitions();

View File

@ -15,7 +15,7 @@
</div>
<button type="button" class="btn btn-success" @onclick="ExportModule">@Localizer["Export"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
<NavLink class="btn btn-secondary" href="@PageState.ReturnUrl">@SharedLocalizer["Cancel"]</NavLink>
@code {
private string _content = string.Empty;

View File

@ -17,7 +17,7 @@
</div>
<button type="button" class="btn btn-success" @onclick="ImportModule">@Localizer["Import"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
<NavLink class="btn btn-secondary" href="@PageState.ReturnUrl">@SharedLocalizer["Cancel"]</NavLink>
</form>
@code {

View File

@ -3,132 +3,139 @@
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IThemeService ThemeService
@inject IPageService PageService
@inject IModuleService ModuleService
@inject IPageModuleService PageModuleService
@inject IStringLocalizer<Settings> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
<TabStrip>
<TabPanel Name="Settings" Heading="Settings" ResourceKey="Settings">
@if (_containers != null)
{
<div class="container">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="module" HelpText="The name of the module" ResourceKey="Module">Module: </Label>
<div class="col-sm-9">
<input id="module" type="text" class="form-control" @bind="@_module" disabled />
@if (_initialized)
{
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
<TabStrip ActiveTab="@_activetab">
<TabPanel Name="Settings" Heading="Settings" ResourceKey="Settings">
@if (_containers != null)
{
<div class="container">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="module" HelpText="The name of the module" ResourceKey="Module">Module: </Label>
<div class="col-sm-9">
<input id="module" type="text" class="form-control" @bind="@_module" disabled />
</div>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="title" HelpText="Enter the title of the module" ResourceKey="Title">Title: </Label>
<div class="col-sm-9">
<input id="title" type="text" class="form-control" @bind="@_title" required />
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="title" HelpText="Enter the title of the module" ResourceKey="Title">Title: </Label>
<div class="col-sm-9">
<input id="title" type="text" class="form-control" @bind="@_title" required />
</div>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="pane" HelpText="The pane where the module will be displayed" ResourceKey="Pane">Pane: </Label>
<div class="col-sm-9">
<select class="form-select" @bind="@_pane">
@foreach (string pane in PageState.Page.Panes)
{
<option value="@pane">@pane Pane</option>
}
</select>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="container" HelpText="Select the module's container" ResourceKey="Container">Container: </Label>
<div class="col-sm-9">
<select id="container" class="form-select" @bind="@_containerType" required>
@foreach (var container in _containers)
{
<option value="@container.TypeName">@container.Name</option>
}
</select>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="effectiveDate" HelpText="The date that this module is active" ResourceKey="EffectiveDate">Effective Date: </Label>
<div class="col-sm-9">
<input type="date" id="effectiveDate" class="form-control" @bind="@_effectivedate" />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="expiryDate" HelpText="The date that this module expires" ResourceKey="ExpiryDate">Expiry Date: </Label>
<div class="col-sm-9">
<input type="date" id="expiryDate" class="form-control" @bind="@_expirydate" />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="allpages" HelpText="Indicate if this module should be displayed on all pages" ResourceKey="DisplayOnAllPages">Display On All Pages? </Label>
<div class="col-sm-9">
<select id="allpages" class="form-select" @bind="@_allPages" required>
<option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option>
</select>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="page" HelpText="The page that the module is located on" ResourceKey="Page">Page: </Label>
<div class="col-sm-9">
<select id="page" class="form-select" @bind="@_pageId" required>
@if (PageState.Page.UserId != null)
{
<option value="@PageState.Page.PageId">@(PageState.Page.Name)</option>
}
else
{
foreach (Page p in PageState.Pages)
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="pane" HelpText="The pane where the module will be displayed" ResourceKey="Pane">Pane: </Label>
<div class="col-sm-9">
<select class="form-select" @bind="@_pane">
@foreach (string pane in PageState.Page.Panes)
{
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, p.PermissionList))
<option value="@pane">@pane Pane</option>
}
</select>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="container" HelpText="Select the module's container" ResourceKey="Container">Container: </Label>
<div class="col-sm-9">
<select id="container" class="form-select" @bind="@_containerType" required>
@foreach (var container in _containers)
{
<option value="@container.TypeName">@container.Name</option>
}
</select>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="effectiveDate" HelpText="The date that this module is active" ResourceKey="EffectiveDate">Effective Date: </Label>
<div class="col-sm-9">
<input type="date" id="effectiveDate" class="form-control" @bind="@_effectivedate" />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="expiryDate" HelpText="The date that this module expires" ResourceKey="ExpiryDate">Expiry Date: </Label>
<div class="col-sm-9">
<input type="date" id="expiryDate" class="form-control" @bind="@_expirydate" />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="allpages" HelpText="Indicate if this module should be displayed on all pages" ResourceKey="DisplayOnAllPages">Display On All Pages? </Label>
<div class="col-sm-9">
<select id="allpages" class="form-select" @bind="@_allPages" required>
<option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option>
</select>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="page" HelpText="The page that the module is located on" ResourceKey="Page">Page: </Label>
<div class="col-sm-9">
<select id="page" class="form-select" @bind="@_pageId" required>
@if (PageState.Page.UserId != null)
{
<option value="@PageState.Page.PageId">@(PageState.Page.Name)</option>
}
else
{
if (_pages != null)
{
<option value="@p.PageId">@(new string('-', p.Level * 2))@(p.Name)</option>
foreach (Page p in _pages)
{
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, p.PermissionList))
{
<option value="@p.PageId">@(new string('-', p.Level * 2))@(p.Name)</option>
}
}
}
}
}
</select>
</select>
</div>
</div>
</div>
</div>
}
</TabPanel>
<TabPanel Name="Permissions" Heading="Permissions" ResourceKey="Permissions">
@if (_permissions != null)
{
<div class="container">
<div class="row mb-1 align-items-center">
<PermissionGrid EntityName="@EntityNames.Module" PermissionNames="@_permissionNames" PermissionList="@_permissions" @ref="_permissionGrid" />
}
</TabPanel>
<TabPanel Name="Permissions" Heading="Permissions" ResourceKey="Permissions">
@if (_permissions != null)
{
<div class="container">
<div class="row mb-1 align-items-center">
<PermissionGrid EntityName="@EntityNames.Module" PermissionNames="@_permissionNames" PermissionList="@_permissions" @ref="_permissionGrid" />
</div>
</div>
</div>
}
</TabPanel>
@if (_moduleSettingsType != null)
{
<TabPanel Name="ModuleSettings" Heading="@_moduleSettingsTitle" ResourceKey="ModuleSettings">
@ModuleSettingsComponent
</TabPanel>
}
</TabPanel>
@if (_moduleSettingsType != null)
{
<TabPanel Name="ModuleSettings" Heading="@_moduleSettingsTitle" ResourceKey="ModuleSettings">
@ModuleSettingsComponent
</TabPanel>
}
@if (_containerSettingsType != null)
{
<TabPanel Name="ContainerSettings" Heading="Container Settings" ResourceKey="ContainerSettings">
@ContainerSettingsComponent
</TabPanel>
}
</TabStrip>
<br />
<button type="button" class="btn btn-success" @onclick="SaveModule">@SharedLocalizer["Save"]</button>
<NavLink class="btn btn-secondary" href="@PageState.ReturnUrl">@SharedLocalizer["Cancel"]</NavLink>
<br />
<br />
<AuditInfo CreatedBy="@createdby" CreatedOn="@createdon" ModifiedBy="@modifiedby" ModifiedOn="@modifiedon"></AuditInfo>
</form>
@if (_containerSettingsType != null)
{
<TabPanel Name="ContainerSettings" Heading="Container Settings" ResourceKey="ContainerSettings">
@ContainerSettingsComponent
</TabPanel>
}
</TabStrip>
<br />
<button type="button" class="btn btn-success" @onclick="SaveModule">@SharedLocalizer["Save"]</button>
<NavLink class="btn btn-secondary" href="@PageState.ReturnUrl">@SharedLocalizer["Cancel"]</NavLink>
<br />
<br />
<AuditInfo CreatedBy="@createdby" CreatedOn="@createdon" ModifiedBy="@modifiedby" ModifiedOn="@modifiedon"></AuditInfo>
</form>
}
@code {
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
private bool _initialized = false;
private ElementReference form;
private bool validated = false;
private List<ThemeControl> _containers = new List<ThemeControl>();
@ -154,11 +161,13 @@
private DateTime modifiedon;
private DateTime? _effectivedate = null;
private DateTime? _expirydate = null;
private List<Page> _pages;
private string _activetab = "";
protected override void OnInitialized()
protected override async Task OnInitializedAsync()
{
SetModuleTitle(Localizer["ModuleSettings.Title"]);
_module = ModuleState.ModuleDefinition.Name;
_title = ModuleState.Title;
_moduleSettingsTitle = Localizer["ModuleSettings.Heading"];
_pane = ModuleState.Pane;
@ -173,10 +182,11 @@
modifiedon = ModuleState.ModifiedOn;
_effectivedate = Utilities.UtcAsLocalDate(ModuleState.EffectiveDate);
_expirydate = Utilities.UtcAsLocalDate(ModuleState.ExpiryDate);
_pages = await PageService.GetPagesAsync(PageState.Site.SiteId);
if (ModuleState.ModuleDefinition != null)
{
_module = ModuleState.ModuleDefinition.Name;
_permissionNames = ModuleState.ModuleDefinition?.PermissionNames;
if (!string.IsNullOrEmpty(ModuleState.ModuleDefinition.SettingsType))
@ -226,10 +236,13 @@
};
}
}
_initialized = true;
}
private async Task SaveModule()
{
validated = true;
var interop = new Interop(JSRuntime);
if (await interop.FormValid(form))
@ -250,21 +263,21 @@
pagemodule.ExpiryDate = Utilities.LocalDateAndTimeAsUtc(_expirydate);
pagemodule.ContainerType = (_containerType != "-") ? _containerType : string.Empty;
if (!string.IsNullOrEmpty(pagemodule.ContainerType) && pagemodule.ContainerType == PageState.Page.DefaultContainerType)
{
pagemodule.ContainerType = string.Empty;
}
if (!string.IsNullOrEmpty(pagemodule.ContainerType) && pagemodule.ContainerType == PageState.Site.DefaultContainerType)
{
pagemodule.ContainerType = string.Empty;
}
await PageModuleService.UpdatePageModuleAsync(pagemodule);
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
{
pagemodule.ContainerType = string.Empty;
}
if (!string.IsNullOrEmpty(pagemodule.ContainerType) && pagemodule.ContainerType == PageState.Site.DefaultContainerType)
{
pagemodule.ContainerType = string.Empty;
}
await PageModuleService.UpdatePageModuleAsync(pagemodule);
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
var module = ModuleState;
module.AllPages = bool.Parse(_allPages);
module.PageModuleId = ModuleState.PageModuleId;
module.PermissionList = _permissionGrid.GetPermissionList();
await ModuleService.UpdateModuleAsync(module);
var module = ModuleState;
module.AllPages = bool.Parse(_allPages);
module.PageModuleId = ModuleState.PageModuleId;
module.PermissionList = _permissionGrid.GetPermissionList();
await ModuleService.UpdateModuleAsync(module);
if (_moduleSettingsType != null)
{
@ -289,11 +302,13 @@
}
else
{
_activetab = "Settings";
AddModuleMessage(Localizer["Message.Required.Title"], MessageType.Warning);
}
}
else
{
_activetab = "Settings";
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
}
}

View File

@ -16,7 +16,7 @@
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="name" HelpText="Enter the page name" ResourceKey="Name">Name: </Label>
<div class="col-sm-9">
<input id="name" class="form-control" @bind="@_name" required />
<input id="name" class="form-control" @bind="@_name" maxlength="50" required />
</div>
</div>
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
@ -26,7 +26,7 @@
<div class="col-sm-9">
<select id="parent" class="form-select" value="@_parentid" @onchange="(e => ParentChanged(e))" required>
<option value="-1">&lt;@Localizer["SiteRoot"]&gt;</option>
@foreach (Page page in PageState.Pages)
@foreach (Page page in _pages)
{
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, page.PermissionList))
{
@ -101,13 +101,13 @@
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="path" HelpText="Optionally enter a url path for this page (ie. home ). If you do not provide a url path, the page name will be used. If the page is intended to be the root path specify '/'." ResourceKey="UrlPath">Url Path: </Label>
<div class="col-sm-9">
<input id="path" class="form-control" @bind="@_path" />
<input id="path" class="form-control" @bind="@_path" maxlength="256" />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="url" HelpText="Optionally enter a url which this page should redirect to when a user navigates to it" ResourceKey="Redirect">Redirect: </Label>
<div class="col-sm-9">
<input id="url" class="form-control" @bind="@_url" />
<input id="url" class="form-control" @bind="@_url" maxlength="500" />
</div>
</div>
<div class="row mb-1 align-items-center">
@ -147,7 +147,7 @@
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" 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>
<div class="col-sm-9">
<input id="title" class="form-control" @bind="@_title" />
<input id="title" class="form-control" @bind="@_title" maxlength="200" />
</div>
</div>
<div class="row mb-1 align-items-center">
@ -156,7 +156,14 @@
<select id="theme" class="form-select" value="@_themetype" @onchange="(e => ThemeChanged(e))" required>
@foreach (var theme in _themes)
{
<option value="@theme.TypeName">@theme.Name</option>
@if (theme.TypeName == PageState.Site.DefaultThemeType)
{
<option value="@theme.TypeName">*@theme.Name*</option>
}
else
{
<option value="@theme.TypeName">@theme.Name</option>
}
}
</select>
</div>
@ -179,13 +186,13 @@
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="headcontent" HelpText="Optionally enter content to be included in the page head (ie. meta, link, or script tags)" ResourceKey="HeadContent">Head Content: </Label>
<div class="col-sm-9">
<textarea id="headcontent" class="form-control" @bind="@_headcontent" rows="3"></textarea>
<textarea id="headcontent" class="form-control" @bind="@_headcontent" rows="3" maxlength="4000"></textarea>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="bodycontent" HelpText="Optionally enter content to be included in the page body (ie. script tags)" ResourceKey="BodyContent">Body Content: </Label>
<div class="col-sm-9">
<textarea id="bodycontent" class="form-control" @bind="@_bodycontent" rows="3"></textarea>
<textarea id="bodycontent" class="form-control" @bind="@_bodycontent" rows="3" maxlength="4000"></textarea>
</div>
</div>
</div>
@ -213,6 +220,7 @@
private bool validated = false;
private List<ThemeControl> _themes = new List<ThemeControl>();
private List<ThemeControl> _containers = new List<ThemeControl>();
private List<Page> _pages;
private int _pageId;
private string _name;
private string _parentid = "-1";
@ -243,6 +251,8 @@
{
try
{
_pages = await PageService.GetPagesAsync(PageState.Site.SiteId);
if (PageState.QueryString.ContainsKey("id"))
{
_pageId = Int32.Parse(PageState.QueryString["id"]);
@ -263,7 +273,7 @@
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, _themetype);
_containertype = PageState.Site.DefaultContainerType;
_children = new List<Page>();
foreach (Page p in PageState.Pages.Where(item => (_parentid == "-1" && item.ParentId == null) || (item.ParentId == int.Parse(_parentid))))
foreach (Page p in _pages.Where(item => (_parentid == "-1" && item.ParentId == null) || (item.ParentId == int.Parse(_parentid))))
{
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.PermissionList))
{
@ -293,7 +303,7 @@
{
_parentid = (string)e.Value;
_children = new List<Page>();
foreach (Page p in PageState.Pages.Where(item => (_parentid == "-1" && item.ParentId == null) || (item.ParentId == int.Parse(_parentid))))
foreach (Page p in _pages.Where(item => (_parentid == "-1" && item.ParentId == null) || (item.ParentId == int.Parse(_parentid))))
{
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.PermissionList))
{
@ -306,10 +316,11 @@
{
await logger.LogError(ex, "Error Loading Child Pages For Parent {PageId} {Error}", _parentid, ex.Message);
AddModuleMessage(Localizer["Error.ChildPage.Load"], MessageType.Error);
await ScrollToPageTop();
}
}
private void ThemeChanged(ChangeEventArgs e)
private async Task ThemeChanged(ChangeEventArgs e)
{
_themetype = (string)e.Value;
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, _themetype);
@ -320,6 +331,7 @@
if (ThemeService.GetTheme(PageState.Site.Themes, _themetype)?.ThemeName != ThemeService.GetTheme(PageState.Site.Themes, PageState.Site.DefaultThemeType)?.ThemeName)
{
AddModuleMessage(Localizer["ThemeChanged.Message"], MessageType.Warning);
await ScrollToPageTop();
}
}
@ -335,6 +347,7 @@
if (!Utilities.ValidateEffectiveExpiryDates(_effectivedate, _expirydate))
{
AddModuleMessage(SharedLocalizer["Message.EffectiveExpiryDateError"], MessageType.Warning);
await ScrollToPageTop();
return;
}
if (!string.IsNullOrEmpty(_themetype) && !string.IsNullOrEmpty(_containertype))
@ -371,7 +384,7 @@
}
else
{
Page parent = PageState.Pages.FirstOrDefault(item => item.PageId == page.ParentId);
Page parent = _pages.FirstOrDefault(item => item.PageId == page.ParentId);
if (parent.Path == string.Empty)
{
page.Path = Utilities.GetFriendlyUrl(parent.Name) + "/" + Utilities.GetFriendlyUrl(_path);
@ -382,16 +395,17 @@
}
}
var _pages = await PageService.GetPagesAsync(PageState.Site.SiteId);
if (_pages.Any(item => item.Path == page.Path))
{
AddModuleMessage(string.Format(Localizer["Message.Page.Exists"], _path), MessageType.Warning);
await ScrollToPageTop();
return;
}
if (page.ParentId == null && Constants.ReservedRoutes.Contains(page.Name.ToLower()))
{
AddModuleMessage(string.Format(Localizer["Message.Page.Reserved"], page.Name), MessageType.Warning);
await ScrollToPageTop();
return;
}
@ -402,11 +416,11 @@
page.Order = 0;
break;
case "<":
child = PageState.Pages.Where(item => item.PageId == _childid).FirstOrDefault();
child = _pages.Where(item => item.PageId == _childid).FirstOrDefault();
page.Order = child.Order - 1;
break;
case ">":
child = PageState.Pages.Where(item => item.PageId == _childid).FirstOrDefault();
child = _pages.Where(item => item.PageId == _childid).FirstOrDefault();
page.Order = child.Order + 1;
break;
case ">>":
@ -449,7 +463,7 @@
await logger.LogInformation("Page Added {Page}", page);
if (!string.IsNullOrEmpty(PageState.ReturnUrl))
{
NavigationManager.NavigateTo(page.Path, true); // redirect to page added and reload
NavigationManager.NavigateTo(NavigateUrl(page.Path), true); // redirect to page added and reload
}
else
{
@ -459,6 +473,7 @@
else
{
AddModuleMessage(Localizer["Message.Required.PageInfo"], MessageType.Warning);
await ScrollToPageTop();
}
}
@ -466,11 +481,13 @@
{
await logger.LogError(ex, "Error Saving Page {Page} {Error}", page, ex.Message);
AddModuleMessage(Localizer["Error.Page.Save"], MessageType.Error);
await ScrollToPageTop();
}
}
else
{
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
await ScrollToPageTop();
}
}

View File

@ -5,7 +5,8 @@
@inject NavigationManager NavigationManager
@inject IPageService PageService
@inject IPageModuleService PageModuleService
@inject IThemeService ThemeService
@inject IModuleService ModuleService
@inject IThemeService ThemeService
@inject ISystemService SystemService
@inject IStringLocalizer<Edit> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer
@ -31,7 +32,7 @@
<div class="col-sm-9">
<select id="parent" class="form-select" value="@_parentid" @onchange="(e => ParentChanged(e))" required>
<option value="-1">&lt;@Localizer["SiteRoot"]&gt;</option>
@foreach (Page page in PageState.Pages)
@foreach (Page page in _pages)
{
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, page.PermissionList) && page.PageId != _pageId)
{
@ -115,7 +116,7 @@
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="path" HelpText="Optionally enter a url path for this page (ie. home ). If you do not provide a url path, the page name will be used. If the page is intended to be the root path specify '/'." ResourceKey="UrlPath">Url Path: </Label>
<Label Class="col-sm-3" For="path" HelpText="Optionally enter a url path for this page (ie. home ). If you do not provide a url path, the page name will be used. Please note that spaces and punctuation will be replaced by a dash. If the page is intended to be the root path specify '/'." ResourceKey="UrlPath">Url Path: </Label>
<div class="col-sm-9">
<input id="path" class="form-control" @bind="@_path" maxlength="256" />
</div>
@ -171,7 +172,14 @@
<select id="theme" class="form-select" value="@_themetype" @onchange="(e => ThemeChanged(e))" required>
@foreach (var theme in _themes)
{
<option value="@theme.TypeName">@theme.Name</option>
@if (theme.TypeName == PageState.Site.DefaultThemeType)
{
<option value="@theme.TypeName">*@theme.Name*</option>
}
else
{
<option value="@theme.TypeName">@theme.Name</option>
}
}
</select>
</div>
@ -197,13 +205,13 @@
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="headcontent" HelpText="Optionally enter content to be included in the page head (ie. meta, link, or script tags)" ResourceKey="HeadContent">Head Content: </Label>
<div class="col-sm-9">
<textarea id="headcontent" class="form-control" @bind="@_headcontent" rows="3"></textarea>
<textarea id="headcontent" class="form-control" @bind="@_headcontent" rows="3" maxlength="4000"></textarea>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="bodycontent" HelpText="Optionally enter content to be included in the page body (ie. script tags)" ResourceKey="BodyContent">Body Content: </Label>
<div class="col-sm-9">
<textarea id="bodycontent" class="form-control" @bind="@_bodycontent" rows="3"></textarea>
<textarea id="bodycontent" class="form-control" @bind="@_bodycontent" rows="3" maxlength="4000"></textarea>
</div>
</div>
</div>
@ -255,13 +263,26 @@
<input id="title" class="form-control" @bind="@_title" maxlength="200" />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="path" HelpText="Provide a url path for your personalized page. Please note that spaces and punctuation will be replaced by a dash." ResourceKey="PersonalizedUrlPath">Url Path: </Label>
<div class="col-sm-9">
<input id="path" class="form-control" @bind="@_path" maxlength="256" />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="theme" HelpText="Select the theme for this page" ResourceKey="Theme">Theme: </Label>
<div class="col-sm-9">
<select id="theme" class="form-select" @bind="@_themetype" required>
@foreach (var theme in _themes)
{
<option value="@theme.TypeName">@theme.Name</option>
@if (theme.TypeName == PageState.Site.DefaultThemeType)
{
<option value="@theme.TypeName">*@theme.Name*</option>
}
else
{
<option value="@theme.TypeName">@theme.Name</option>
}
}
</select>
</div>
@ -302,6 +323,7 @@
private bool validated = false;
private List<ThemeControl> _themes = new List<ThemeControl>();
private List<ThemeControl> _containers = new List<ThemeControl>();
private List<Page> _pages;
private int _pageId;
private string _name;
private string _currentparentid;
@ -345,6 +367,7 @@
{
try
{
_pages = await PageService.GetPagesAsync(PageState.Site.SiteId);
_pageId = Int32.Parse(PageState.QueryString["id"]);
_page = await PageService.GetPageAsync(_pageId);
_icons = await SystemService.GetIconsAsync();
@ -360,10 +383,10 @@
else
{
_parentid = _page.ParentId.ToString();
_parent = PageState.Pages.FirstOrDefault(item => item.PageId == _page.ParentId);
_parent = _pages.FirstOrDefault(item => item.PageId == _page.ParentId);
}
_children = new List<Page>();
foreach (Page p in PageState.Pages.Where(item => (_parentid == "-1" && item.ParentId == null) || (item.ParentId == int.Parse(_parentid, CultureInfo.InvariantCulture))))
foreach (Page p in _pages.Where(item => (_parentid == "-1" && item.ParentId == null) || (item.ParentId == int.Parse(_parentid, CultureInfo.InvariantCulture))))
{
if (p.PageId != _pageId && UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.PermissionList))
{
@ -415,7 +438,8 @@
_permissions = _page.PermissionList;
// page modules
_pageModules = PageState.Modules.Where(m => m.PageId == _page.PageId).ToList();
var modules = await ModuleService.GetModulesAsync(PageState.Site.SiteId);
_pageModules = modules.Where(item => item.PageId == _page.PageId && !item.IsDeleted).ToList();
// audit
_createdby = _page.CreatedBy;
@ -447,7 +471,7 @@
{
_parentid = (string)e.Value;
_children = new List<Page>();
foreach (Page p in PageState.Pages.Where(item => (_parentid == "-1" && item.ParentId == null) || (item.ParentId == int.Parse(_parentid))))
foreach (Page p in _pages.Where(item => (_parentid == "-1" && item.ParentId == null) || (item.ParentId == int.Parse(_parentid))))
{
if (p.PageId != _pageId && UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.PermissionList))
{
@ -461,10 +485,11 @@
{
await logger.LogError(ex, "Error Loading Child Pages For Parent {PageId} {Error}", _parentid, ex.Message);
AddModuleMessage(Localizer["Error.ChildPage.Load"], MessageType.Error);
await ScrollToPageTop();
}
}
private void ThemeChanged(ChangeEventArgs e)
private async Task ThemeChanged(ChangeEventArgs e)
{
_themetype = (string)e.Value;
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, _themetype);
@ -476,6 +501,7 @@
if (ThemeService.GetTheme(PageState.Site.Themes, _themetype)?.ThemeName != ThemeService.GetTheme(PageState.Site.Themes, PageState.Site.DefaultThemeType)?.ThemeName)
{
AddModuleMessage(Localizer["ThemeChanged.Message"], MessageType.Warning);
await ScrollToPageTop();
}
}
@ -513,6 +539,7 @@
if (!Utilities.ValidateEffectiveExpiryDates(_effectivedate, _expirydate))
{
AddModuleMessage(SharedLocalizer["Message.EffectiveExpiryDateError"], MessageType.Warning);
await ScrollToPageTop();
return;
}
if (!string.IsNullOrEmpty(_themetype) && _containertype != "-")
@ -549,7 +576,7 @@
}
else
{
Page parent = PageState.Pages.FirstOrDefault(item => item.PageId == _page.ParentId);
Page parent = _pages.FirstOrDefault(item => item.PageId == _page.ParentId);
if (parent.Path == string.Empty)
{
_page.Path = Utilities.GetFriendlyUrl(parent.Name) + "/" + Utilities.GetFriendlyUrl(_path);
@ -560,16 +587,17 @@
}
}
var _pages = await PageService.GetPagesAsync(PageState.Site.SiteId);
if (_pages.Any(item => item.Path == _page.Path && item.PageId != _page.PageId))
{
AddModuleMessage(string.Format(Localizer["Mesage.Page.PathExists"], _path), MessageType.Warning);
await ScrollToPageTop();
return;
}
if (_page.ParentId == null && Constants.ReservedRoutes.Contains(_page.Name.ToLower()))
{
AddModuleMessage(string.Format(Localizer["Message.Page.Reserved"], _page.Name), MessageType.Warning);
await ScrollToPageTop();
return;
}
@ -582,11 +610,11 @@
_page.Order = 0;
break;
case "<":
child = PageState.Pages.FirstOrDefault(item => item.PageId == _childid);
child = _pages.FirstOrDefault(item => item.PageId == _childid);
if (child != null) _page.Order = child.Order - 1;
break;
case ">":
child = PageState.Pages.FirstOrDefault(item => item.PageId == _childid);
child = _pages.FirstOrDefault(item => item.PageId == _childid);
if (child != null) _page.Order = child.Order + 1;
break;
case ">>":
@ -654,17 +682,20 @@
else
{
AddModuleMessage(Localizer["Message.Required.PageInfo"], MessageType.Warning);
await ScrollToPageTop();
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Saving Page {Page} {Error}", _page, ex.Message);
AddModuleMessage(Localizer["Error.Page.Save"], MessageType.Error);
await ScrollToPageTop();
}
}
else
{
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
await ScrollToPageTop();
}
}

View File

@ -5,11 +5,11 @@
@inject IStringLocalizer<Index> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer
@if (PageState.Pages != null && UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
@if (_pages != null && UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
{
<ActionLink Action="Add" Text="Add Page" ResourceKey="AddPage" />
<Pager Items="@PageState.Pages.Where(item => !item.IsDeleted)" SearchProperties="Name">
<Pager Items="@_pages.Where(item => !item.IsDeleted)" SearchProperties="Name">
<Header>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
@ -28,6 +28,21 @@
@code {
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
private List<Page> _pages;
protected override async Task OnInitializedAsync()
{
try
{
_pages = await PageService.GetPagesAsync(PageState.Site.SiteId);
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading Pages {Error}", ex.Message);
AddModuleMessage(Localizer["Error.Page.Load"], MessageType.Error);
}
}
private async Task DeletePage(Page page)
{
try

View File

@ -13,71 +13,71 @@
}
else
{
<TabStrip>
<TabPanel Name="Pages" ResourceKey="Pages" Heading="Pages">
@if (!_pages.Where(item => item.IsDeleted).Any())
{
<br />
<p>@Localizer["NoPage.Deleted"]</p>
}
else
{
<Pager Items="@_pages.Where(item => item.IsDeleted)" CurrentPage="@_pagePage.ToString()" OnPageChange="OnPageChangePage">
<Header>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>@SharedLocalizer["Name"]</th>
<th>@Localizer["DeletedBy"]</th>
<th>@Localizer["DeletedOn"]</th>
</Header>
<Row>
<TabStrip>
<TabPanel Name="Pages" ResourceKey="Pages" Heading="Pages">
@if (!_pages.Where(item => item.IsDeleted).Any())
{
<br />
<p>@Localizer["NoPage.Deleted"]</p>
}
else
{
<Pager Items="@_pages.Where(item => item.IsDeleted).OrderByDescending(item => item.DeletedOn)" CurrentPage="@_pagePage.ToString()" OnPageChange="OnPageChangePage">
<Header>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>@SharedLocalizer["Path"]</th>
<th>@Localizer["DeletedBy"]</th>
<th>@Localizer["DeletedOn"]</th>
</Header>
<Row>
<td><button type="button" @onclick="@(() => RestorePage(context))" class="btn btn-success" title="Restore">@Localizer["Restore"]</button></td>
<td><ActionDialog Header="Delete Page" Message="@string.Format(Localizer["Confirm.Page.Delete"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeletePage(context))" ResourceKey="DeletePage" /></td>
<td>@context.Name</td>
<td>@context.DeletedBy</td>
<td>@context.DeletedOn</td>
</Row>
</Pager>
<br />
<ActionDialog Header="Remove All Deleted Pages" Message="Are You Sure You Wish To Permanently Remove All Deleted Pages?" Action="Remove All Deleted Pages" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteAllPages())" ResourceKey="DeleteAllPages" />
}
</TabPanel>
<TabPanel Name="Modules" ResourceKey="Modules" Heading="Modules">
@if (!_modules.Where(item => item.IsDeleted).Any())
{
<br />
<p>@Localizer["NoModule.Deleted"]</p>
}
else
{
<Pager Items="@_modules.Where(item => item.IsDeleted)" CurrentPage="@_pageModule.ToString()" OnPageChange="OnPageChangeModule">
<Header>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>@Localizer["Page"]</th>
<th>@Localizer["Module"]</th>
<th>@Localizer["DeletedBy"]</th>
<th>@Localizer["DeletedOn"]</th>
</Header>
<Row>
<td><button type="button" @onclick="@(() => RestoreModule(context))" class="btn btn-success" title="Restore">@Localizer["Restore"]</button></td>
<td><ActionDialog Header="Delete Module" Message="@string.Format(Localizer["Confirm.Module.Delete"], context.Title)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteModule(context))" ResourceKey="DeleteModule" /></td>
<td>@_pages.Find(item => item.PageId == context.PageId).Name</td>
<td>@context.Title</td>
<td>@context.DeletedBy</td>
<td>@context.DeletedOn</td>
</Row>
</Pager>
<br />
<ActionDialog Header="Remove All Deleted Modules" Message="Are You Sure You Wish To Permanently Remove All Deleted Modules?" Action="Remove All Deleted Modules" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteAllModules())" ResourceKey="DeleteAllModules" />
}
</TabPanel>
</TabStrip>
<td><ActionDialog Header="Delete Page" Message="@string.Format(Localizer["Confirm.Page.Delete"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeletePage(context))" ResourceKey="DeletePage" /></td>
<td>@context.Path</td>
<td>@context.DeletedBy</td>
<td>@context.DeletedOn</td>
</Row>
</Pager>
<br />
<ActionDialog Header="Remove All Deleted Pages" Message="Are You Sure You Wish To Permanently Remove All Deleted Pages?" Action="Remove All Deleted Pages" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteAllPages())" ResourceKey="DeleteAllPages" />
}
</TabPanel>
<TabPanel Name="Modules" ResourceKey="Modules" Heading="Modules">
@if (!_modules.Where(item => item.IsDeleted).Any())
{
<br />
<p>@Localizer["NoModule.Deleted"]</p>
}
else
{
<Pager Items="@_modules.Where(item => item.IsDeleted).OrderByDescending(item => item.DeletedOn)" CurrentPage="@_pageModule.ToString()" OnPageChange="OnPageChangeModule">
<Header>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>@Localizer["Page"]</th>
<th>@Localizer["Module"]</th>
<th>@Localizer["DeletedBy"]</th>
<th>@Localizer["DeletedOn"]</th>
</Header>
<Row>
<td><button type="button" @onclick="@(() => RestoreModule(context))" class="btn btn-success" title="Restore">@Localizer["Restore"]</button></td>
<td><ActionDialog Header="Delete Module" Message="@string.Format(Localizer["Confirm.Module.Delete"], context.Title)" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteModule(context))" ResourceKey="DeleteModule" /></td>
<td>@_pages.Find(item => item.PageId == context.PageId).Name</td>
<td>@context.Title</td>
<td>@context.DeletedBy</td>
<td>@context.DeletedOn</td>
</Row>
</Pager>
<br />
<ActionDialog Header="Remove All Deleted Modules" Message="Are You Sure You Wish To Permanently Remove All Deleted Modules?" Action="Remove All Deleted Modules" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteAllModules())" ResourceKey="DeleteAllModules" />
}
</TabPanel>
</TabStrip>
}
@code {
private List<Page> _pages;
private List<Module> _modules;
private List<Page> _pages;
private List<Module> _modules;
private int _pagePage = 1;
private int _pageModule = 1;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
@ -105,12 +105,25 @@ else
{
try
{
page.IsDeleted = false;
await PageService.UpdatePageAsync(page);
await logger.LogInformation("Page Restored {Page}", page);
await Load();
StateHasChanged();
NavigationManager.NavigateTo(NavigateUrl());
var validated = true;
if (page.ParentId != null)
{
var parent = _pages.Find(item => item.PageId == page.ParentId);
validated = !parent.IsDeleted;
}
if (validated)
{
page.IsDeleted = false;
await PageService.UpdatePageAsync(page);
await logger.LogInformation("Page Restored {Page}", page);
AddModuleMessage(Localizer["Success.Page.Restore"], MessageType.Success);
await Load();
StateHasChanged();
}
else
{
AddModuleMessage(Localizer["Message.Page.Restore"], MessageType.Warning);
}
}
catch (Exception ex)
{
@ -125,9 +138,9 @@ else
{
await PageService.DeletePageAsync(page.PageId);
await logger.LogInformation("Page Permanently Deleted {Page}", page);
AddModuleMessage(Localizer["Success.Page.Delete"], MessageType.Success);
await Load();
StateHasChanged();
NavigationManager.NavigateTo(NavigateUrl());
}
catch (Exception ex)
{
@ -148,10 +161,10 @@ else
}
await logger.LogInformation("Pages Permanently Deleted");
AddModuleMessage(Localizer["Success.Pages.Delete"], MessageType.Success);
await Load();
HideProgressIndicator();
StateHasChanged();
NavigationManager.NavigateTo(NavigateUrl());
}
catch (Exception ex)
{
@ -169,6 +182,7 @@ else
pagemodule.IsDeleted = false;
await PageModuleService.UpdatePageModuleAsync(pagemodule);
await logger.LogInformation("Module Restored {Module}", module);
AddModuleMessage(Localizer["Success.Module.Restore"], MessageType.Success);
await Load();
StateHasChanged();
}
@ -185,6 +199,7 @@ else
{
await PageModuleService.DeletePageModuleAsync(module.PageModuleId);
await logger.LogInformation("Module Permanently Deleted {Module}", module);
AddModuleMessage(Localizer["Success.Module.Delete"], MessageType.Success);
await Load();
StateHasChanged();
}
@ -205,6 +220,7 @@ else
await PageModuleService.DeletePageModuleAsync(module.PageModuleId);
}
await logger.LogInformation("Modules Permanently Deleted");
AddModuleMessage(Localizer["Success.Modules.Delete"], MessageType.Success);
await Load();
HideProgressIndicator();
StateHasChanged();

View File

@ -11,65 +11,64 @@
{
if (!_userCreated)
{
<AuthorizeView Roles="@RoleNames.Registered">
<Authorizing>
<text>...</text>
</Authorizing>
<Authorized>
<ModuleMessage Message="@Localizer["Info.Registration.Exists"]" Type="MessageType.Info" />
</Authorized>
<NotAuthorized>
<ModuleMessage Message="@_passwordrequirements" Type="MessageType.Info" />
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
<div class="container">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="username" HelpText="Your username. Note that this field can not be modified once it is saved." ResourceKey="Username"></Label>
<div class="col-sm-9">
<input id="username" class="form-control" @bind="@_username" maxlength="256" required />
</div>
if (PageState.User != null)
{
<ModuleMessage Message="@Localizer["Info.Registration.Exists"]" Type="MessageType.Info" />
}
else
{
<ModuleMessage Message="@_passwordrequirements" Type="MessageType.Info" />
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
<div class="container">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="username" HelpText="Your username. Note that this field can not be modified once it is saved." ResourceKey="Username"></Label>
<div class="col-sm-9">
<input id="username" class="form-control" @bind="@_username" maxlength="256" required />
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="password" HelpText="Please choose a sufficiently secure password and enter it here" ResourceKey="Password"></Label>
<div class="col-sm-9">
<div class="input-group">
<input id="password" type="@_passwordtype" class="form-control" @bind="@_password" autocomplete="new-password" required />
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword" tabindex="-1">@_togglepassword</button>
</div>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="confirm" HelpText="Enter your password again to confirm it matches the value entered above" ResourceKey="Confirm"></Label>
<div class="col-sm-9">
<div class="input-group">
<input id="confirm" type="@_passwordtype" class="form-control" @bind="@_confirm" autocomplete="new-password" required />
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword" tabindex="-1">@_togglepassword</button>
</div>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="email" HelpText="Your email address where you wish to receive notifications" ResourceKey="Email"></Label>
<div class="col-sm-9">
<input id="email" class="form-control" @bind="@_email" maxlength="256" required />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="displayname" HelpText="Your full name" ResourceKey="DisplayName"></Label>
<div class="col-sm-9">
<input id="displayname" class="form-control" @bind="@_displayname" maxlength="50" />
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="password" HelpText="Please choose a sufficiently secure password and enter it here" ResourceKey="Password"></Label>
<div class="col-sm-9">
<div class="input-group">
<input id="password" type="@_passwordtype" class="form-control" @bind="@_password" autocomplete="new-password" required />
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword" tabindex="-1">@_togglepassword</button>
</div>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="confirm" HelpText="Enter your password again to confirm it matches the value entered above" ResourceKey="Confirm"></Label>
<div class="col-sm-9">
<div class="input-group">
<input id="confirm" type="@_passwordtype" class="form-control" @bind="@_confirm" autocomplete="new-password" required />
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword" tabindex="-1">@_togglepassword</button>
</div>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="email" HelpText="Your email address where you wish to receive notifications" ResourceKey="Email"></Label>
<div class="col-sm-9">
<input id="email" class="form-control" @bind="@_email" maxlength="256" required />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="displayname" HelpText="Your full name" ResourceKey="DisplayName"></Label>
<div class="col-sm-9">
<input id="displayname" class="form-control" @bind="@_displayname" maxlength="50" />
</div>
</div>
</div>
<br />
<button type="button" class="btn btn-primary" @onclick="Register">@Localizer["Register"]</button>
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
@if (_allowsitelogin)
{
<br />
<button type="button" class="btn btn-primary" @onclick="Register">@Localizer["Register"]</button>
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
@if (_allowsitelogin)
{
<br /><br />
<NavLink href="@NavigateUrl("login")">@Localizer["Login"]</NavLink>
}
</form>
</NotAuthorized>
</AuthorizeView>
<br />
<NavLink href="@NavigateUrl("login")">@Localizer["Login"]</NavLink>
}
</form>
}
}
}
else

View File

@ -58,7 +58,7 @@ else
<td>@context.EffectiveDate</td>
<td>@context.ExpiryDate</td>
<td>
<ActionDialog Header="Remove User" Message="@string.Format(Localizer["Confirm.User.DeleteRole"], context.User.DisplayName)" Action="Delete" Security="SecurityAccessLevel.Edit" Class="btn btn-danger" OnClick="@(async () => await DeleteUserRole(context.UserRoleId))" Disabled="@(context.Role.IsAutoAssigned || context.User.Username == UserNames.Host || context.User.UserId == PageState.User.UserId)" ResourceKey="DeleteUserRole" />
<ActionDialog Header="Remove User" Message="@string.Format(Localizer["Confirm.User.DeleteRole"], context.User.DisplayName)" Action="Delete" Security="SecurityAccessLevel.Edit" Class="btn btn-danger" OnClick="@(async () => await DeleteUserRole(context.UserRoleId))" Disabled="@(context.User.Username == UserNames.Host || context.User.UserId == PageState.User.UserId)" ResourceKey="DeleteUserRole" />
</td>
</Row>
</Pager>
@ -180,27 +180,28 @@ else
private async Task DeleteUserRole(int UserRoleId)
{
validated = true;
var interop = new Interop(JSRuntime);
if (await interop.FormValid(form))
try
{
try
var userrole = await UserRoleService.GetUserRoleAsync(UserRoleId);
if (userrole.Role.Name == RoleNames.Registered)
{
userrole.ExpiryDate = DateTime.UtcNow;
await UserRoleService.UpdateUserRoleAsync(userrole);
await logger.LogInformation("User {Username} Expired From Role {Role}", userrole.User.Username, userrole.Role.Name);
}
else
{
await UserRoleService.DeleteUserRoleAsync(UserRoleId);
await logger.LogInformation("User Removed From Role {UserRoleId}", UserRoleId);
AddModuleMessage(Localizer["Confirm.User.RoleRemoved"], MessageType.Success);
await GetUserRoles();
StateHasChanged();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Removing User From Role {UserRoleId} {Error}", UserRoleId, ex.Message);
AddModuleMessage(Localizer["Error.User.RemoveRole"], MessageType.Error);
await logger.LogInformation("User {Username} Removed From Role {Role}", userrole.User.Username, userrole.Role.Name);
}
AddModuleMessage(Localizer["Confirm.User.RoleRemoved"], MessageType.Success);
await GetUserRoles();
StateHasChanged();
}
else
catch (Exception ex)
{
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
await logger.LogError(ex, "Error Removing User From Role {UserRoleId} {Error}", UserRoleId, ex.Message);
AddModuleMessage(Localizer["Error.User.RemoveRole"], MessageType.Error);
}
}
}

View File

@ -0,0 +1,119 @@
@namespace Oqtane.Modules.Admin.Search
@inherits ModuleBase
@inject ISettingService SettingService
@inject IStringLocalizer<Index> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer
<div class="container">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="searchprovider" HelpText="Specify the search provider for this site" ResourceKey="SearchProvider">Search Provider: </Label>
<div class="col-sm-9">
<input id="searchprovider" class="form-control" @bind="@_searchProvider" />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="enabled" HelpText="Specify if search indexing is enabled" ResourceKey="Enabled">Indexing Enabled? </Label>
<div class="col-sm-9">
<select id="enabled" class="form-select" @bind="@_enabled">
<option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option>
</select>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="lastindexedon" HelpText="The date/time which the site was last indexed on" ResourceKey="LastIndexedOn">Last Indexed: </Label>
<div class="col-sm-9">
<input id="lastindexedon" class="form-control" @bind="@_lastIndexedOn" />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="ignorepages" HelpText="Comma delimited list of pages which should be ignored (based on their path)" ResourceKey="IgnorePages">Ignore Pages: </Label>
<div class="col-sm-9">
<textarea id="ignorepages" class="form-control" @bind="@_ignorePages" rows="3"></textarea>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="ignoreentities" HelpText="Comma delimited list of entities which should be ignored" ResourceKey="IgnoreEntities">Ignore Entities: </Label>
<div class="col-sm-9">
<textarea id="ignoreentities" class="form-control" @bind="@_ignoreEntities" rows="3"></textarea>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="minimumwordlength" HelpText="Minimum length of a word to be indexed" ResourceKey="MinimumWordLength">Word Length: </Label>
<div class="col-sm-9">
<input id="minimumwordlength" class="form-control" type="number" min="0" step="1" @bind="@_minimumWordLength" />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="ignorewords" HelpText="Comma delimited list of words which should be ignored" ResourceKey="IgnoreWords">Ignore Words: </Label>
<div class="col-sm-9">
<textarea id="ignorewords" class="form-control" @bind="@_ignoreWords" rows="3"></textarea>
</div>
</div>
</div>
<br /><br />
<button type="button" class="btn btn-success" @onclick="Save">@SharedLocalizer["Save"]</button>
<ActionDialog Header="Reindex" Message="Are You Sure You Wish To Reindex Search Content?" Action="Reindex" Class="btn btn-danger" OnClick="@(async () => await Reindex())" ResourceKey="Reindex" />
<br /><br />
@code {
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
private string _searchProvider;
private string _enabled = "True";
private string _lastIndexedOn = "";
private string _ignorePages = "";
private string _ignoreEntities = "File";
private string _minimumWordLength = "3";
private string _ignoreWords = "the,be,to,of,and,a,i,in,that,have,it,for,not,on,with,he,as,you,do,at,this,but,his,by,from,they,we,say,her,she,or,an,will,my,one,all,would,there,their,what,so,up,out,if,about,who,get,which,go,me,when,make,can,like,time,no,just,him,know,take,people,into,year,your,good,some,could,them,see,other,than,then,now,look,only,come,its,over,think,also,back,after,use,two,how,our,work,first,well,way,even,new,want,because,any,these,give,day,most,us";
protected override async Task OnInitializedAsync()
{
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
_searchProvider = SettingService.GetSetting(settings, "Search_SearchProvider", Constants.DefaultSearchProviderName);
_enabled = SettingService.GetSetting(settings, "Search_Enabled", _enabled);
_lastIndexedOn = SettingService.GetSetting(settings, "Search_LastIndexedOn", _lastIndexedOn);
_ignorePages = SettingService.GetSetting(settings, "Search_IgnorePages", _ignorePages);
_ignoreEntities = SettingService.GetSetting(settings, "Search_IgnoreEntities", _ignoreEntities);
_minimumWordLength = SettingService.GetSetting(settings, "Search_MininumWordLength", _minimumWordLength);
_ignoreWords = SettingService.GetSetting(settings, "Search_IgnoreWords", _ignoreWords);
}
private async Task Save()
{
try
{
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
settings = SettingService.SetSetting(settings, "Search_SearchProvider", _searchProvider);
settings = SettingService.SetSetting(settings, "Search_Enabled", _enabled);
settings = SettingService.SetSetting(settings, "Search_LastIndexedOn", _lastIndexedOn, true);
settings = SettingService.SetSetting(settings, "Search_IgnorePages", _ignorePages, true);
settings = SettingService.SetSetting(settings, "Search_IgnoreEntities", _ignoreEntities, true);
settings = SettingService.SetSetting(settings, "Search_MininumWordLength", _minimumWordLength, true);
settings = SettingService.SetSetting(settings, "Search_IgnoreWords", _ignoreWords, true);
await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId);
AddModuleMessage(Localizer["Success.Save"], MessageType.Success);
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Saving Search Settings {Error}", ex.Message);
AddModuleMessage(Localizer["Error.Save"], MessageType.Error);
}
}
private async Task Reindex()
{
try
{
_lastIndexedOn = DateTime.MinValue.ToString();
await Save();
AddModuleMessage(Localizer["Message.Reindex"], MessageType.Success);
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Saving Search Settings {Error}", ex.Message);
AddModuleMessage(Localizer["Error.Save"], MessageType.Error);
}
}
}

View File

@ -0,0 +1,142 @@
@using Oqtane.Services
@using System.Net
@namespace Oqtane.Modules.Admin.SearchResults
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject ISearchResultsService SearchResultsService
@inject ISettingService SettingService
@inject IStringLocalizer<Index> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer
@attribute [StreamRendering] // attribute allows the progress indicator to be displayed
<div class="search-result-container">
<div class="row">
<div class="col">
<form method="post" @formname="SearchResultsForm" @onsubmit="Search" data-enhance>
<div class="input-group mb-3">
<span class="input-group-text">@Localizer["SearchLabel"]</span>
<input type="hidden" name="@Constants.RequestVerificationToken" value="@SiteState.AntiForgeryToken" />
<input type="text" name="keywords" class="form-control shadow-none" maxlength="50"
aria-label="Keywords"
placeholder="@Localizer["SearchPlaceholder"]"
@bind="@_keywords">
<button class="btn btn-primary" type="submit">@SharedLocalizer["Search"]</button>
<a class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Reset"]</a>
</div>
</form>
</div>
</div>
<div class="row">
<div class="col-md-12 mb-3">
@if (_loading)
{
<div class="app-progress-indicator"></div>
}
else
{
@if (_searchResults != null && _searchResults.Results != null)
{
if (_searchResults.Results.Any())
{
<Pager Items="@_searchResults?.Results"
Format="Grid"
Columns="1"
Toolbar="Bottom"
Parameters="@($"q={_keywords}")">
<Row>
<div class="search-item mb-2">
<h4 class="mb-1"><a href="@context.Url">@context.Title</a></h4>
<p class="mb-0 text-muted">@((MarkupString)context.Snippet)</p>
</div>
</Row>
</Pager>
}
else
{
<div class="alert alert-info show mt-3" role="alert">
@Localizer["NoResult"]
</div>
}
}
<div class="clearfix"></div>
}
</div>
</div>
</div>
@code {
public override string RenderMode => RenderModes.Static;
private string _includeEntities;
private string _excludeEntities;
private string _fromDate;
private string _toDate;
private string _pageSize;
private string _sortField;
private string _sortOrder;
private string _bodyLength;
private string _keywords;
private SearchResults _searchResults;
private bool _loading;
[SupplyParameterFromForm(FormName = "SearchResultsForm")]
public string KeyWords { get => ""; set => _keywords = value; }
protected override async Task OnInitializedAsync()
{
_includeEntities = SettingService.GetSetting(ModuleState.Settings, "SearchResults_IncludeEntities", "");
_excludeEntities = SettingService.GetSetting(ModuleState.Settings, "SearchResults_ExcludeEntities", "");
_fromDate = SettingService.GetSetting(ModuleState.Settings, "SearchResults_FromDate", DateTime.MinValue.ToString());
_toDate = SettingService.GetSetting(ModuleState.Settings, "SearchResults_ToDate", DateTime.MaxValue.ToString());
_pageSize = SettingService.GetSetting(ModuleState.Settings, "SearchResults_PageSize", int.MaxValue.ToString());
_sortField = SettingService.GetSetting(ModuleState.Settings, "SearchResults_SortField", "Relevance");
_sortOrder = SettingService.GetSetting(ModuleState.Settings, "SearchResults_SortOrder", "Descending");
_bodyLength = SettingService.GetSetting(ModuleState.Settings, "SearchResults_BodyLength", "255");
if (_keywords == null && PageState.QueryString.ContainsKey("q"))
{
_keywords = WebUtility.UrlDecode(PageState.QueryString["q"]);
await PerformSearch();
}
}
private void Search()
{
NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, $"page=1&q={WebUtility.UrlEncode(_keywords)}"));
}
private async Task PerformSearch()
{
_loading = true;
StateHasChanged();
if (!string.IsNullOrEmpty(_keywords))
{
var searchQuery = new SearchQuery
{
SiteId = PageState.Site.SiteId,
Alias = PageState.Alias,
Keywords = _keywords,
IncludeEntities = _includeEntities,
ExcludeEntities = _excludeEntities,
FromDate = (!string.IsNullOrEmpty(_fromDate)) ? DateTime.Parse(_fromDate) : DateTime.MinValue,
ToDate = (!string.IsNullOrEmpty(_toDate)) ? DateTime.Parse(_toDate) : DateTime.MaxValue,
PageSize = (!string.IsNullOrEmpty(_pageSize)) ? int.Parse(_pageSize) : int.MaxValue,
PageIndex = 0,
SortField = (!string.IsNullOrEmpty(_sortField)) ? (SearchSortField)Enum.Parse(typeof(SearchSortField), _sortField) : SearchSortField.Relevance,
SortOrder = (!string.IsNullOrEmpty(_sortOrder)) ? (SearchSortOrder)Enum.Parse(typeof(SearchSortOrder), _sortOrder) : SearchSortOrder.Descending,
BodyLength = (!string.IsNullOrEmpty(_bodyLength)) ? int.Parse(_bodyLength) : 255
};
_searchResults = await SearchResultsService.GetSearchResultsAsync(searchQuery);
}
else
{
AddModuleMessage(Localizer["NoCriteria"], MessageType.Info, "bottom");
}
_loading = false;
StateHasChanged();
}
}

View File

@ -0,0 +1,19 @@
using Oqtane.Documentation;
using Oqtane.Models;
using Oqtane.Shared;
namespace Oqtane.Modules.Admin.SearchResults
{
[PrivateApi("Mark this as private, since it's not very useful in the public docs")]
public class ModuleInfo : IModule
{
public ModuleDefinition ModuleDefinition => new ModuleDefinition
{
Name = "Search Results",
Description = "Search Results",
Categories = "Admin",
Version = Constants.Version,
SettingsType = "Oqtane.Modules.Admin.SearchResults.Settings, Oqtane.Client"
};
}
}

View File

@ -0,0 +1,123 @@
@namespace Oqtane.Modules.Admin.SearchResults
@inherits ModuleBase
@inject ISettingService SettingService
@implements Oqtane.Interfaces.ISettingsControl
@inject IStringLocalizer<Settings> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
<div class="container">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="includeentities" ResourceKey="IncludeEntities" ResourceType="@resourceType" HelpText="Comma delimited list of entities to include in the search results. By default all entities will be included.">Include Entities: </Label>
<div class="col-sm-9">
<input id="includeentities" type="text" class="form-control" @bind="@_includeEntities" />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="excludeentities" ResourceKey="ExcludeEntities" ResourceType="@resourceType" HelpText="Comma delimited list of entities to exclude from search results. By default no entities will be excluded.">Exclude Entities: </Label>
<div class="col-sm-9">
<input id="excludeentities" class="form-control" @bind="@_excludeEntities" />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="daterange" ResourceKey="DateRange" ResourceType="@resourceType" HelpText="Enter the date range for search results. The default includes all content.">Date Range: </Label>
<div class="col-sm-9">
<div class="input-group">
<input type="date" class="form-control" @bind="@_fromDate" />
<span class="input-group-text">@Localizer["To"]</span>
<input type="date" class="form-control" @bind="@_toDate" />
</div>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="pagesize" ResourceKey="PageSize" ResourceType="@resourceType" HelpText="The maximum number of search results to retrieve. The default is unlimited.">Page Size: </Label>
<div class="col-sm-9">
<input id="pagesize" type="text" class="form-control" @bind="@_pageSize" />
</div>
</div>
<hr />
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="sortfield" ResourceKey="SortField" ResourceType="@resourceType" HelpText="Specify the default sort field">Sort By: </Label>
<div class="col-sm-9">
<select id="softfield" class="form-select" @bind="@_sortField">
<option value="Relevance">@Localizer["Relevance"]</option>
<option value="Title">@Localizer["Title"]</option>
<option value="LastModified">@Localizer["LastModified"]</option>
</select>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="sortorder" ResourceKey="SortOrder" ResourceType="@resourceType" HelpText="Specify the default sort order">Sort Order: </Label>
<div class="col-sm-9">
<select id="softorder" class="form-select" @bind="@_sortOrder">
<option value="Ascending">@Localizer["Ascending"]</option>
<option value="Descending">@Localizer["Descending"]</option>
</select>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="bodylength" ResourceKey="BodyLength" ResourceType="@resourceType" HelpText="The number of characters displayed for each search result summary. The default is 255 characters.">Body Size: </Label>
<div class="col-sm-9">
<input id="bodylength" type="text" class="form-control" @bind="@_bodyLength" />
</div>
</div>
</div>
</form>
@code {
private string resourceType = "Oqtane.Modules.Admin.SearchResults.Settings, Oqtane.Client"; // for localization
private ElementReference form;
private bool validated = false;
private string _includeEntities;
private string _excludeEntities;
private DateTime? _fromDate = null;
private DateTime? _toDate = null;
private string _pageSize;
private string _sortField;
private string _sortOrder;
private string _bodyLength;
protected override void OnInitialized()
{
try
{
_includeEntities = SettingService.GetSetting(ModuleState.Settings, "SearchResults_IncludeEntities", "");
_excludeEntities = SettingService.GetSetting(ModuleState.Settings, "SearchResults_ExcludeEntities", "");
var fromDate = SettingService.GetSetting(ModuleState.Settings, "SearchResults_FromDate", "");
_fromDate = (string.IsNullOrEmpty(fromDate)) ? null : DateTime.Parse(fromDate);
var toDate = SettingService.GetSetting(ModuleState.Settings, "SearchResults_ToDate", "");
_toDate = (string.IsNullOrEmpty(toDate)) ? null : DateTime.Parse(toDate);
_pageSize = SettingService.GetSetting(ModuleState.Settings, "SearchResults_PageSize", "");
_sortField = SettingService.GetSetting(ModuleState.Settings, "SearchResults_SortField", "Relevance");
_sortOrder = SettingService.GetSetting(ModuleState.Settings, "SearchResults_SortOrder", "Descending");
_bodyLength = SettingService.GetSetting(ModuleState.Settings, "SearchResults_BodyLength", "255");
}
catch (Exception ex)
{
AddModuleMessage(ex.Message, MessageType.Error);
}
}
public async Task UpdateSettings()
{
try
{
var settings = await SettingService.GetModuleSettingsAsync(ModuleState.ModuleId);
settings = SettingService.SetSetting(settings, "SearchResults_IncludeEntities", _includeEntities);
settings = SettingService.SetSetting(settings, "SearchResults_ExcludeEntities", _excludeEntities);
settings = SettingService.SetSetting(settings, "SearchResults_From", _fromDate.ToString());
settings = SettingService.SetSetting(settings, "SearchResults_To", _toDate.ToString());
settings = SettingService.SetSetting(settings, "SearchResults_PageSize", _pageSize);
settings = SettingService.SetSetting(settings, "SearchResults_SortField", _sortField);
settings = SettingService.SetSetting(settings, "SearchResults_SortOrder", _sortOrder);
settings = SettingService.SetSetting(settings, "SearchResults_BodyLength", _bodyLength);
await SettingService.UpdateModuleSettingsAsync(settings, ModuleState.ModuleId);
}
catch (Exception ex)
{
AddModuleMessage(ex.Message, MessageType.Error);
}
}
}

View File

@ -1,16 +1,20 @@
@namespace Oqtane.Modules.Admin.Site
@inherits ModuleBase
@using System.Text.RegularExpressions
@using Microsoft.Extensions.DependencyInjection
@inject NavigationManager NavigationManager
@inject ISiteService SiteService
@inject IPageService PageService
@inject ITenantService TenantService
@inject IDatabaseService DatabaseService
@inject IAliasService AliasService
@inject IThemeService ThemeService
@inject ISettingService SettingService
@inject IServiceProvider ServiceProvider
@inject IStringLocalizer<Index> Localizer
@inject INotificationService NotificationService
@inject IStringLocalizer<SharedResources> SharedLocalizer
@inject IOutputCacheService CacheService
@if (_initialized)
{
@ -27,34 +31,35 @@
<div class="col-sm-9">
<select id="homepage" class="form-select" @bind="@_homepageid" required>
<option value="-">&lt;@SharedLocalizer["Not Specified"]&gt;</option>
@foreach (Page page in PageState.Pages)
@foreach (Page page in _pages)
{
if (UserSecurity.ContainsRole(page.PermissionList, PermissionNames.View, RoleNames.Everyone))
{
<option value="@(page.PageId)">@(new string('-', page.Level * 2))@(page.Name)</option>
}
if (UserSecurity.ContainsRole(page.PermissionList, PermissionNames.View, RoleNames.Everyone))
{
<option value="@(page.PageId)">@(new string('-', page.Level * 2))@(page.Name)</option>
}
}
</select>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="isDeleted" HelpText="Is this site deleted?" ResourceKey="IsDeleted">Deleted? </Label>
<div class="col-sm-9">
<select id="isDeleted" class="form-select" @bind="@_isdeleted" required>
<option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option>
</select>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="sitemap" HelpText="The site map url for this site which can be submitted to search engines for indexing" ResourceKey="SiteMap">Site Map: </Label>
<div class="col-sm-9">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="isDeleted" HelpText="Is this site deleted?" ResourceKey="IsDeleted">Deleted? </Label>
<div class="col-sm-9">
<select id="isDeleted" class="form-select" @bind="@_isdeleted" required>
<option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option>
</select>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="sitemap" HelpText="The site map url for this site which can be submitted to search engines for indexing. The sitemap is cached for 5 minutes and the cache can be manually cleared." ResourceKey="SiteMap">Site Map: </Label>
<div class="col-sm-9">
<div class="input-group">
<input id="sitemap" class="form-control" @bind="@_sitemap" disabled />
<a href="@_sitemap" class="btn btn-secondary" target="_new">@Localizer["Browse"]</a>
<button type="button" class="btn btn-danger" @onclick="EvictSitemapOutputCache">@Localizer["SiteMap.EvictCache"]</button>
</div>
</div>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="siteguid" HelpText="The Unique Identifier For The Site" ResourceKey="SiteGuid">ID: </Label>
<div class="col-sm-9">
@ -69,20 +74,8 @@
</div>
</div>
<br />
<Section Name="Appearance" Heading="Appearance" ResourceKey="Appearance">
<Section Name="Theme" Heading="Theme" ResourceKey="Theme">
<div class="container">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="logo" HelpText="Specify a logo for the site" ResourceKey="Logo">Logo: </Label>
<div class="col-sm-9">
<FileManager FileId="@_logofileid" Filter="@_ImageFiles" @ref="_logofilemanager" />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="favicon" HelpText="Specify a Favicon" ResourceKey="FavoriteIcon">Favicon: </Label>
<div class="col-sm-9">
<FileManager FileId="@_faviconfileid" Filter="ico,png,gif" @ref="_faviconfilemanager" />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="defaultTheme" HelpText="Select the sites default theme" ResourceKey="DefaultTheme">Default Theme: </Label>
<div class="col-sm-9">
@ -123,27 +116,55 @@
</select>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="cookieconsent" HelpText="Specify if cookie consent is enabled on this site. Please note this option must be used in conjunction with a Theme which supports cookie consent." ResourceKey="CookieConsent">Cookie Consent: </Label>
<div class="col-sm-9">
<select id="cookieconsent" class="form-select" @bind="@_cookieconsent">
<option value="">@SharedLocalizer["Disabled"]</option>
<option value="optin">@Localizer["OptIn"]</option>
<option value="optout">@Localizer["OptOut"]</option>
</select>
</div>
</div>
</div>
</Section>
<Section Name="FileExtensions" Heading="File Extensions" ResourceKey="FileExtensions">
<Section Name="Appearance" Heading="Appearance" ResourceKey="Appearance">
<div class="container">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="imageExt" HelpText="Enter a comma separated list of image file extensions" ResourceKey="ImageExtensions">Image Extensions: </Label>
<Label Class="col-sm-3" For="logo" HelpText="Specify a logo for the site" ResourceKey="Logo">Logo: </Label>
<div class="col-sm-9">
<input id="imageExt" spellcheck="false" class="form-control" @bind="@_ImageFiles" />
<FileManager FileId="@_logofileid" Filter="@_imageFiles" @ref="_logofilemanager" />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="uploadableFileExt" HelpText="Enter a comma separated list of uploadable file extensions" ResourceKey="UploadableFileExtensions">Uploadable File Extensions: </Label>
<Label Class="col-sm-3" For="favicon" HelpText="Specify a Favicon" ResourceKey="FavoriteIcon">Favicon: </Label>
<div class="col-sm-9">
<input id="uploadableFileExt" spellcheck="false" class="form-control" @bind="@_UploadableFiles" />
<FileManager FileId="@_faviconfileid" Filter="ico,png,gif" @ref="_faviconfilemanager" />
</div>
</div>
</div>
</Section>
<Section Name="Functionality" Heading="Functionality" ResourceKey="Functionality">
<div class="container">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="textEditor" HelpText="Select the text editor for the site" ResourceKey="TextEditor">Text Editor: </Label>
<div class="col-sm-9">
<select id="textEditor" class="form-select" @bind="@_textEditor" required>
@if (_textEditors != null)
{
@foreach (var textEditor in _textEditors)
{
<option value="@textEditor.Value">@textEditor.Key</option>
}
}
</select>
</div>
</div>
</div>
</Section>
<Section Name="PageContent" Heading="Page Content" ResourceKey="PageContent">
<div class="container">
<div class="row mb-1 align-items-center">
<div class="container">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="headcontent" HelpText="Optionally enter content to be included in the page head (ie. meta, link, or script tags)" ResourceKey="HeadContent">Head Content: </Label>
<div class="col-sm-9">
<textarea id="headcontent" class="form-control" @bind="@_headcontent" rows="3"></textarea>
@ -190,16 +211,16 @@
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="username" HelpText="Enter the username for your SMTP account" ResourceKey="SmtpUsername">Username: </Label>
<div class="col-sm-9">
<input id="username" class="form-control" @bind="@_smtpusername" />
<input id="username" class="form-control" @bind="@_smtpusername" autocomplete="off"/>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="password" HelpText="Enter the password for your SMTP account" ResourceKey="SmtpPassword">Password: </Label>
<div class="col-sm-9">
<div class="input-group">
<input id="password" type="@_smtppasswordtype" class="form-control" @bind="@_smtppassword" />
<div class="input-group">
<input id="password" type="@_smtppasswordtype" class="form-control" @bind="@_smtppassword" autocomplete="off"/>
<button type="button" class="btn btn-secondary" @onclick="@ToggleSMTPPassword" tabindex="-1">@_togglesmtppassword</button>
</div>
</div>
</div>
</div>
<div class="row mb-1 align-items-center">
@ -208,15 +229,15 @@
<input id="sender" class="form-control" @bind="@_smtpsender" />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="relay" HelpText="Only specify this option if you have properly configured an SMTP Relay Service to route your outgoing mail. This option will send notifications from the user's email rather than from the Email Sender specified above." ResourceKey="SmtpRelay">Relay Configured? </Label>
<div class="col-sm-9">
<select id="relay" class="form-select" @bind="@_smtprelay" required>
<option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option>
</select>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="relay" HelpText="Only specify this option if you have properly configured an SMTP Relay Service to route your outgoing mail. This option will send notifications from the user's email rather than from the Email Sender specified above." ResourceKey="SmtpRelay">Relay Configured? </Label>
<div class="col-sm-9">
<select id="relay" class="form-select" @bind="@_smtprelay" required>
<option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option>
</select>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="smtpenabled" HelpText="Specify if SMTP is enabled for this site" ResourceKey="SMTPEnabled">Enabled? </Label>
<div class="col-sm-9">
@ -227,10 +248,10 @@
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="retention" HelpText="Number of days of notifications to retain" ResourceKey="Retention">Retention (Days): </Label>
<div class="col-sm-9">
<Label Class="col-sm-3" For="retention" HelpText="Number of days of notifications to retain" ResourceKey="Retention">Retention (Days): </Label>
<div class="col-sm-9">
<input id="retention" class="form-control" type="number" min="0" step="1" @bind="@_retention" />
</div>
</div>
</div>
<button type="button" class="btn btn-secondary" @onclick="SendEmail">@Localizer["Smtp.TestConfig"]</button>
<br /><br />
@ -263,57 +284,57 @@
</Section>
@if (_aliases != null && UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
{
<Section Name="Aliases" Heading="Aliases" ResourceKey="Aliases">
<div class="container">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="aliases" HelpText="The urls for the site. This can include domain names (ie. domain.com), subdomains (ie. sub.domain.com) or virtual folders (ie. domain.com/folder)." ResourceKey="Aliases">Aliases: </Label>
<div class="col-sm-9">
<button type="button" class="btn btn-primary" @onclick="AddAlias">@SharedLocalizer["Add"]</button>
<Pager Items="@_aliases">
<Header>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>@Localizer["AliasName"]</th>
<th>@Localizer["AliasDefault"]</th>
</Header>
<Row>
@if (context.AliasId != _aliasid)
{
<td>
@if (_aliasid == -1)
{
<button type="button" class="btn btn-primary" @onclick="@(() => EditAlias(context))">@SharedLocalizer["Edit"]</button>
}
</td>
<td>
@if (_aliasid == -1)
{
<ActionDialog Action="Delete" OnClick="@(async () => await DeleteAlias(context))" ResourceKey="DeleteAlias" Class="btn btn-danger" Header="Delete Alias" Message="@string.Format(Localizer["Confirm.Alias.Delete", context.Name])" />
}
</td>
<td>@context.Name</td>
<td>@context.IsDefault</td>
}
else
{
<td><button type="button" class="btn btn-success" @onclick="@(async () => await SaveAlias())">@SharedLocalizer["Save"]</button></td>
<td><button type="button" class="btn btn-secondary" @onclick="@(async () => await CancelAlias())">@SharedLocalizer["Cancel"]</button></td>
<td>
<input id="aliasname" class="form-control" @bind="@_aliasname" />
</td>
<td>
<select id="defaultalias" class="form-select" @bind="@_defaultalias" required>
<option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option>
</select>
</td>
}
</Row>
</Pager>
</div>
</div>
</div>
</Section>
<Section Name="Aliases" Heading="Aliases" ResourceKey="Aliases">
<div class="container">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="aliases" HelpText="The urls for the site. This can include domain names (ie. domain.com), subdomains (ie. sub.domain.com) or virtual folders (ie. domain.com/folder)." ResourceKey="Aliases">Aliases: </Label>
<div class="col-sm-9">
<button type="button" class="btn btn-primary" @onclick="AddAlias">@SharedLocalizer["Add"]</button>
<Pager Items="@_aliases">
<Header>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>@Localizer["AliasName"]</th>
<th>@Localizer["AliasDefault"]</th>
</Header>
<Row>
@if (context.AliasId != _aliasid)
{
<td>
@if (_aliasid == -1)
{
<button type="button" class="btn btn-primary" @onclick="@(() => EditAlias(context))">@SharedLocalizer["Edit"]</button>
}
</td>
<td>
@if (_aliasid == -1)
{
<ActionDialog Action="Delete" OnClick="@(async () => await DeleteAlias(context))" ResourceKey="DeleteAlias" Class="btn btn-danger" Header="Delete Alias" Message="@string.Format(Localizer["Confirm.Alias.Delete", context.Name])" />
}
</td>
<td>@context.Name</td>
<td>@((context.IsDefault) ? SharedLocalizer["Yes"] : SharedLocalizer["No"])</td>
}
else
{
<td><button type="button" class="btn btn-success" @onclick="@(async () => await SaveAlias())">@SharedLocalizer["Save"]</button></td>
<td><button type="button" class="btn btn-secondary" @onclick="@(async () => await CancelAlias())">@SharedLocalizer["Cancel"]</button></td>
<td>
<input id="aliasname" class="form-control" @bind="@_aliasname" />
</td>
<td>
<select id="defaultalias" class="form-select" @bind="@_defaultalias" required>
<option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option>
</select>
</td>
}
</Row>
</Pager>
</div>
</div>
</div>
</Section>
<Section Name="Hosting" Heading="Hosting Model" ResourceKey="Hosting">
<div class="container">
<div class="row mb-1 align-items-center">
@ -359,7 +380,7 @@
<Section Name="TenantInformation" Heading="Database" ResourceKey="TenantInformation">
<div class="container">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="tenant" HelpText="The name of the database used for the site" ResourceKey="Tenant">Database: </Label>
<Label Class="col-sm-3" For="tenant" HelpText="The name of the database used for the site. Note that this is not the physical database name but rather the tenant name which is used within the framework to identify a database." ResourceKey="Tenant">Database: </Label>
<div class="col-sm-9">
<input id="tenant" class="form-control" @bind="@_tenant" readonly />
</div>
@ -371,9 +392,9 @@
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="connectionstring" HelpText="The connection information for the database" ResourceKey="ConnectionString">Connection: </Label>
<Label Class="col-sm-3" For="connectionstring" HelpText="The name of the connection string in appsettings.json which will be used to connect to the database" ResourceKey="ConnectionString">Connection: </Label>
<div class="col-sm-9">
<input id="connectionstring" class="form-control" @bind="@_connectionstring" readonly />
<input id="connectionstring" class="form-control" @bind="@_connectionstring" readonly />
</div>
</div>
</div>
@ -394,12 +415,15 @@
private bool _initialized = false;
private List<ThemeControl> _themes = new List<ThemeControl>();
private List<ThemeControl> _containers = new List<ThemeControl>();
private List<Page> _pages;
private string _name = string.Empty;
private string _homepageid = "-";
private string _isdeleted;
private string _sitemap = "";
private string _siteguid = "";
private string _version = "";
private int _logofileid = -1;
private FileManager _logofilemanager;
private int _faviconfileid = -1;
@ -407,8 +431,15 @@
private string _themetype = "";
private string _containertype = "";
private string _admincontainertype = "";
private string _cookieconsent = "";
private Dictionary<string, string> _textEditors = new Dictionary<string, string>();
private string _textEditor = "";
private string _imageFiles = string.Empty;
private string _headcontent = string.Empty;
private string _bodycontent = string.Empty;
private string _smtphost = string.Empty;
private string _smtpport = string.Empty;
private string _smtpssl = "False";
@ -419,25 +450,28 @@
private string _smtpsender = string.Empty;
private string _smtprelay = "False";
private string _smtpenabled = "True";
private string _ImageFiles = string.Empty;
private string _UploadableFiles = string.Empty;
private int _retention = 30;
private string _pwaisenabled;
private int _pwaappiconfileid = -1;
private FileManager _pwaappiconfilemanager;
private int _pwasplashiconfileid = -1;
private FileManager _pwasplashiconfilemanager;
private List<Alias> _aliases;
private int _aliasid = -1;
private string _aliasname;
private string _defaultalias;
private string _rendermode = RenderModes.Interactive;
private string _runtime = Runtimes.Server;
private string _prerender = "True";
private string _hybrid = "False";
private string _tenant = string.Empty;
private string _database = string.Empty;
private string _connectionstring = string.Empty;
private string _createdby;
private DateTime _createdon;
private string _modifiedby;
@ -451,9 +485,18 @@
{
try
{
if (PageState.QueryString.ContainsKey("updated"))
{
AddModuleMessage(Localizer["Success.Settings.SaveSite"], MessageType.Success);
}
Site site = await SiteService.GetSiteAsync(PageState.Site.SiteId);
if (site != null)
{
var settings = await SettingService.GetSiteSettingsAsync(site.SiteId);
_pages = await PageService.GetPagesAsync(PageState.Site.SiteId);
_name = site.Name;
if (site.HomePageId != null)
{
@ -479,24 +522,23 @@
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, _themetype);
_containertype = (!string.IsNullOrEmpty(site.DefaultContainerType)) ? site.DefaultContainerType : Constants.DefaultContainer;
_admincontainertype = (!string.IsNullOrEmpty(site.AdminContainerType)) ? site.AdminContainerType : Constants.DefaultAdminContainer;
_cookieconsent = SettingService.GetSetting(settings, "CookieConsent", string.Empty);
// functionality
var textEditors = ServiceProvider.GetServices<ITextEditor>();
foreach (var textEditor in textEditors)
{
_textEditors.Add(textEditor.Name, Utilities.GetFullTypeName(textEditor.GetType().AssemblyQualifiedName));
}
_textEditor = SettingService.GetSetting(settings, "TextEditor", Constants.DefaultTextEditor);
_imageFiles = SettingService.GetSetting(settings, "ImageFiles", Constants.ImageFiles);
_imageFiles = (string.IsNullOrEmpty(_imageFiles)) ? Constants.ImageFiles : _imageFiles;
// page content
_headcontent = site.HeadContent;
_bodycontent = site.BodyContent;
// PWA
_pwaisenabled = site.PwaIsEnabled.ToString();
if (site.PwaAppIconFileId != null)
{
_pwaappiconfileid = site.PwaAppIconFileId.Value;
}
if (site.PwaSplashIconFileId != null)
{
_pwasplashiconfileid = site.PwaSplashIconFileId.Value;
}
// SMTP
var settings = await SettingService.GetSiteSettingsAsync(site.SiteId);
_smtphost = SettingService.GetSetting(settings, "SMTPHost", string.Empty);
_smtpport = SettingService.GetSetting(settings, "SMTPPort", string.Empty);
_smtpssl = SettingService.GetSetting(settings, "SMTPSSL", "False");
@ -508,11 +550,16 @@
_smtpenabled = SettingService.GetSetting(settings, "SMTPEnabled", "True");
_retention = int.Parse(SettingService.GetSetting(settings, "NotificationRetention", "30"));
// file extensions
_ImageFiles = SettingService.GetSetting(settings, "ImageFiles", Constants.ImageFiles);
_ImageFiles = (string.IsNullOrEmpty(_ImageFiles)) ? Constants.ImageFiles : _ImageFiles;
_UploadableFiles = SettingService.GetSetting(settings, "UploadableFiles", Constants.UploadableFiles);
_UploadableFiles = (string.IsNullOrEmpty(_UploadableFiles)) ? Constants.UploadableFiles : _UploadableFiles;
// PWA
_pwaisenabled = site.PwaIsEnabled.ToString();
if (site.PwaAppIconFileId != null)
{
_pwaappiconfileid = site.PwaAppIconFileId.Value;
}
if (site.PwaSplashIconFileId != null)
{
_pwasplashiconfileid = site.PwaSplashIconFileId.Value;
}
// aliases
await GetAliases();
@ -532,7 +579,7 @@
if (tenant != null)
{
_tenant = tenant.Name;
_database = _databases.Find(item => item.DBType == tenant.DBType)?.Name;
_database = _databases.Find(item => item.DBType == tenant.DBType && item.Name != "LocalDB")?.Name;
_connectionstring = tenant.DBConnectionString;
}
}
@ -673,160 +720,162 @@
}
}
site = await SiteService.UpdateSiteAsync(site);
site = await SiteService.UpdateSiteAsync(site);
// SMTP
var settings = await SettingService.GetSiteSettingsAsync(site.SiteId);
var settings = await SettingService.GetSiteSettingsAsync(site.SiteId);
settings = SettingService.SetSetting(settings, "SMTPHost", _smtphost, true);
settings = SettingService.SetSetting(settings, "SMTPPort", _smtpport, true);
settings = SettingService.SetSetting(settings, "SMTPSSL", _smtpssl, true);
settings = SettingService.SetSetting(settings, "SMTPUsername", _smtpusername, true);
settings = SettingService.SetSetting(settings, "SMTPPassword", _smtppassword, true);
settings = SettingService.SetSetting(settings, "SMTPSender", _smtpsender, true);
settings = SettingService.SetSetting(settings, "SMTPRelay", _smtprelay, true);
settings = SettingService.SetSetting(settings, "SMTPPort", _smtpport, true);
settings = SettingService.SetSetting(settings, "SMTPSSL", _smtpssl, true);
settings = SettingService.SetSetting(settings, "SMTPUsername", _smtpusername, true);
settings = SettingService.SetSetting(settings, "SMTPPassword", _smtppassword, true);
settings = SettingService.SetSetting(settings, "SMTPSender", _smtpsender, true);
settings = SettingService.SetSetting(settings, "SMTPRelay", _smtprelay, true);
settings = SettingService.SetSetting(settings, "SMTPEnabled", _smtpenabled, true);
settings = SettingService.SetSetting(settings, "SiteGuid", _siteguid, true);
settings = SettingService.SetSetting(settings, "NotificationRetention", _retention.ToString(), true);
// file extensions
settings = SettingService.SetSetting(settings, "ImageFiles", (_ImageFiles != Constants.ImageFiles) ? _ImageFiles.Replace(" ", "") : "", false);
settings = SettingService.SetSetting(settings, "UploadableFiles", (_UploadableFiles != Constants.UploadableFiles) ? _UploadableFiles.Replace(" ", "") : "", false);
//cookie consent
settings = SettingService.SetSetting(settings, "CookieConsent", _cookieconsent);
await SettingService.UpdateSiteSettingsAsync(settings, site.SiteId);
// functionality
settings = SettingService.SetSetting(settings, "TextEditor", _textEditor);
await logger.LogInformation("Site Settings Saved {Site}", site);
await SettingService.UpdateSiteSettingsAsync(settings, site.SiteId);
NavigationManager.NavigateTo(NavigateUrl(), true); // reload
}
}
else
{
AddModuleMessage(Localizer["Message.Required.SiteName"], MessageType.Warning);
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Saving Site {SiteId} {Error}", PageState.Site.SiteId, ex.Message);
AddModuleMessage(Localizer["Error.SaveSite"], MessageType.Error);
}
}
else
{
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
}
}
await logger.LogInformation("Site Settings Saved {Site}", site);
private async Task DeleteSite()
{
try
{
var aliases = await AliasService.GetAliasesAsync();
if (aliases.Any(item => item.SiteId != PageState.Site.SiteId || item.TenantId != PageState.Site.TenantId))
{
await SiteService.DeleteSiteAsync(PageState.Site.SiteId);
await logger.LogInformation("Site Deleted {SiteId}", PageState.Site.SiteId);
NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, "updated=true"), true); // reload
}
}
else
{
AddModuleMessage(Localizer["Message.Required.SiteName"], MessageType.Warning);
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Saving Site {SiteId} {Error}", PageState.Site.SiteId, ex.Message);
AddModuleMessage(Localizer["Error.SaveSite"], MessageType.Error);
}
}
else
{
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
}
}
foreach (Alias alias in aliases.Where(item => item.SiteId == PageState.Site.SiteId && item.TenantId == PageState.Site.TenantId))
{
await AliasService.DeleteAliasAsync(alias.AliasId);
}
private async Task DeleteSite()
{
try
{
var aliases = await AliasService.GetAliasesAsync();
if (aliases.Any(item => item.SiteId != PageState.Site.SiteId || item.TenantId != PageState.Site.TenantId))
{
await SiteService.DeleteSiteAsync(PageState.Site.SiteId);
await logger.LogInformation("Site Deleted {SiteId}", PageState.Site.SiteId);
var redirect = aliases.First(item => item.SiteId != PageState.Site.SiteId || item.TenantId != PageState.Site.TenantId);
NavigationManager.NavigateTo(PageState.Uri.Scheme + "://" + redirect.Name, true);
}
else
{
AddModuleMessage(Localizer["Message.FailAuth.DeleteSite"], MessageType.Warning);
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Deleting Site {SiteId} {Error}", PageState.Site.SiteId, ex.Message);
AddModuleMessage(Localizer["Error.DeleteSite"], MessageType.Error);
}
}
foreach (Alias alias in aliases.Where(item => item.SiteId == PageState.Site.SiteId && item.TenantId == PageState.Site.TenantId))
{
await AliasService.DeleteAliasAsync(alias.AliasId);
}
private async Task SendEmail()
{
if (_smtphost != "" && _smtpport != "" && _smtpsender != "")
{
try
{
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
settings = SettingService.SetSetting(settings, "SMTPHost", _smtphost, true);
settings = SettingService.SetSetting(settings, "SMTPPort", _smtpport, true);
settings = SettingService.SetSetting(settings, "SMTPSSL", _smtpssl, true);
settings = SettingService.SetSetting(settings, "SMTPUsername", _smtpusername, true);
settings = SettingService.SetSetting(settings, "SMTPPassword", _smtppassword, true);
settings = SettingService.SetSetting(settings, "SMTPSender", _smtpsender, true);
await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId);
await logger.LogInformation("Site SMTP Settings Saved");
var redirect = aliases.First(item => item.SiteId != PageState.Site.SiteId || item.TenantId != PageState.Site.TenantId);
NavigationManager.NavigateTo(PageState.Uri.Scheme + "://" + redirect.Name, true);
}
else
{
AddModuleMessage(Localizer["Message.FailAuth.DeleteSite"], MessageType.Warning);
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Deleting Site {SiteId} {Error}", PageState.Site.SiteId, ex.Message);
AddModuleMessage(Localizer["Error.DeleteSite"], MessageType.Error);
}
}
await NotificationService.AddNotificationAsync(new Notification(PageState.Site.SiteId, PageState.User, PageState.Site.Name + " SMTP Configuration Test", "SMTP Server Is Configured Correctly."));
AddModuleMessage(Localizer["Info.Smtp.SaveSettings"], MessageType.Info);
private async Task SendEmail()
{
if (_smtphost != "" && _smtpport != "" && _smtpsender != "")
{
try
{
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
settings = SettingService.SetSetting(settings, "SMTPHost", _smtphost, true);
settings = SettingService.SetSetting(settings, "SMTPPort", _smtpport, true);
settings = SettingService.SetSetting(settings, "SMTPSSL", _smtpssl, true);
settings = SettingService.SetSetting(settings, "SMTPUsername", _smtpusername, true);
settings = SettingService.SetSetting(settings, "SMTPPassword", _smtppassword, true);
settings = SettingService.SetSetting(settings, "SMTPSender", _smtpsender, true);
await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId);
await logger.LogInformation("Site SMTP Settings Saved");
await NotificationService.AddNotificationAsync(new Notification(PageState.Site.SiteId, PageState.User, PageState.Site.Name + " SMTP Configuration Test", "SMTP Server Is Configured Correctly."));
AddModuleMessage(Localizer["Info.Smtp.SaveSettings"], MessageType.Info);
await ScrollToPageTop();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Testing SMTP Configuration");
AddModuleMessage(Localizer["Error.Smtp.TestConfig"], MessageType.Error);
}
}
else
{
AddModuleMessage(Localizer["Message.Required.Smtp"], MessageType.Warning);
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Testing SMTP Configuration");
AddModuleMessage(Localizer["Error.Smtp.TestConfig"], MessageType.Error);
}
}
else
{
AddModuleMessage(Localizer["Message.Required.Smtp"], MessageType.Warning);
}
}
private void ToggleSMTPPassword()
{
if (_smtppasswordtype == "password")
{
_smtppasswordtype = "text";
_togglesmtppassword = SharedLocalizer["HidePassword"];
}
else
{
_smtppasswordtype = "password";
_togglesmtppassword = SharedLocalizer["ShowPassword"];
}
}
private void ToggleSMTPPassword()
{
if (_smtppasswordtype == "password")
{
_smtppasswordtype = "text";
_togglesmtppassword = SharedLocalizer["HidePassword"];
}
else
{
_smtppasswordtype = "password";
_togglesmtppassword = SharedLocalizer["ShowPassword"];
}
}
private async Task GetAliases()
{
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
{
_aliases = await AliasService.GetAliasesAsync();
_aliases = _aliases.Where(item => item.SiteId == PageState.Site.SiteId && item.TenantId == PageState.Site.TenantId).OrderBy(item => item.AliasId).ToList();
}
}
private async Task GetAliases()
{
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
{
_aliases = await AliasService.GetAliasesAsync();
_aliases = _aliases.Where(item => item.SiteId == PageState.Site.SiteId && item.TenantId == PageState.Site.TenantId).OrderBy(item => item.AliasId).ToList();
}
}
private void AddAlias()
{
_aliases.Add(new Alias { AliasId = 0, Name = "", IsDefault = false });
_aliasid = 0;
_aliasname = "";
_defaultalias = "False";
StateHasChanged();
}
private void AddAlias()
{
_aliases.Add(new Alias { AliasId = 0, Name = "", IsDefault = false });
_aliasid = 0;
_aliasname = "";
_defaultalias = "False";
StateHasChanged();
}
private void EditAlias(Alias alias)
{
_aliasid = alias.AliasId;
_aliasname = alias.Name;
_defaultalias = alias.IsDefault.ToString();
StateHasChanged();
}
private void EditAlias(Alias alias)
{
_aliasid = alias.AliasId;
_aliasname = alias.Name;
_defaultalias = alias.IsDefault.ToString();
StateHasChanged();
}
private async Task DeleteAlias(Alias alias)
{
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
{
await AliasService.DeleteAliasAsync(alias.AliasId);
await GetAliases();
StateHasChanged();
}
}
private async Task DeleteAlias(Alias alias)
{
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
{
await AliasService.DeleteAliasAsync(alias.AliasId);
await GetAliases();
StateHasChanged();
}
}
private async Task SaveAlias()
{
@ -878,11 +927,16 @@
}
}
private async Task CancelAlias()
{
await GetAliases();
_aliasid = -1;
_aliasname = "";
StateHasChanged();
}
private async Task CancelAlias()
{
await GetAliases();
_aliasid = -1;
_aliasname = "";
StateHasChanged();
}
private async Task EvictSitemapOutputCache() {
await CacheService.EvictByTag(Constants.SitemapOutputCacheTag);
AddModuleMessage(Localizer["Success.SiteMap.CacheEvicted"], MessageType.Success);
}
}

View File

@ -109,7 +109,7 @@ else
<hr class="app-rule" />
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="name" HelpText="Enter the name for the database" ResourceKey="TenantName">Name: </Label>
<Label Class="col-sm-3" For="name" HelpText="Enter the name for the database. Note that this will be the tenant name which is used within the framework to identify the database." ResourceKey="TenantName">Name: </Label>
<div class="col-sm-9">
<input id="name" class="form-control" @bind="@_tenantName" maxlength="100" required />
</div>

View File

@ -83,24 +83,15 @@ else
{
@if (_connection != "-")
{
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="databasetype" HelpText="The database type" ResourceKey="DatabaseType">Type: </Label>
<div class="col-sm-9">
@if (_databases != null)
{
<select id="databasetype" class="form-select" @bind="@_databasetype" required>
<option value="-">&lt;@Localizer["Type.Select"]&gt;</option>
@foreach (var database in _databases)
{
<option value="@database.Name">@Localizer[@database.Name]</option>
}
</select>
}
</div>
</div>
@if (!string.IsNullOrEmpty(_tenant))
{
<div class="row mb-1 align-items-center">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="databasetype" HelpText="The database type" ResourceKey="DatabaseType">Type: </Label>
<div class="col-sm-9">
<input id="databasetype" class="form-control" @bind="@_databasetype" readonly />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="tenant" HelpText="The database using this connection" ResourceKey="Tenant">Database: </Label>
<div class="col-sm-9">
<input id="tenant" class="form-control" @bind="@_tenant" readonly />
@ -204,12 +195,12 @@ else
{
_connectionstring = _connections[_connection].ToString();
_tenant = "";
_databasetype = "-";
_databasetype = "";
var tenant = _tenants.FirstOrDefault(item => item.DBConnectionString == _connection);
if (tenant != null)
{
_tenant = tenant.Name;
_databasetype = _databases.FirstOrDefault(item => item.DBType == tenant.DBType).Name;
_databasetype = _databases.FirstOrDefault(item => item.DBType == tenant.DBType && item.Name != "LocalDB").Name;
}
}
else

View File

@ -133,16 +133,30 @@
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="packageregistryurl" HelpText="Specify The Package Manager Service For Installing Modules, Themes, And Translations. If This Field Is Blank It Means The Package Manager Service Is Disabled For This Installation." ResourceKey="PackageManager">Package Manager: </Label>
<Label Class="col-sm-3" For="cachecontrol" HelpText="Provide a Cache-Control directive for static assets. For example 'public, max-age=60' indicates that static assets should be cached for 60 seconds. A blank value indicates caching is not enabled." ResourceKey="CacheControl">Static Asset Caching: </Label>
<div class="col-sm-9">
<input id="cachecontrol" class="form-control" @bind="@_cachecontrol" />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="packageregistryurl" HelpText="Specify The Url Of The Package Manager Service For Installing Modules, Themes, And Translations. If This Field Is Blank It Means The Package Manager Service Is Disabled For This Installation." ResourceKey="PackageManager">Package Manager Url: </Label>
<div class="col-sm-9">
<input id="packageregistryurl" class="form-control" @bind="@_packageregistryurl" />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="packageregistryemail" HelpText="Specify The Email Address Of The User Account Used For Interacting With The Package Manager Service. This Account Is Used For Managing Packages Across Multiple Installations." ResourceKey="PackageManagerEmail">Package Manager Email: </Label>
<div class="col-sm-9">
<input id="packageregistryemail" class="form-control" @bind="@_packageregistryemail" />
</div>
</div>
</div>
<br /><br />
<button type="button" class="btn btn-success" @onclick="SaveConfig">@SharedLocalizer["Save"]</button>&nbsp;
<a class="btn btn-primary" href="swagger/index.html" target="_new">@Localizer["Access.ApiFramework"]</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" />
<br /><br />
<a class="btn btn-primary" href="swagger/index.html" target="_new">@Localizer["Swagger"]</a>&nbsp;
<a class="btn btn-secondary" href="api/endpoint" target="_new">@Localizer["Endpoints"]</a>
</TabPanel>
<TabPanel Name="Log" Heading="Log" ResourceKey="Log">
<div class="container">
@ -179,9 +193,11 @@
private string _logginglevel = string.Empty;
private string _notificationlevel = string.Empty;
private string _swagger = string.Empty;
private string _cachecontrol = string.Empty;
private string _packageregistryurl = string.Empty;
private string _packageregistryemail = string.Empty;
private string _log = string.Empty;
private string _log = string.Empty;
protected override async Task OnInitializedAsync()
{
@ -209,9 +225,11 @@
_detailederrors = systeminfo["DetailedErrors"].ToString();
_logginglevel = systeminfo["Logging:LogLevel:Default"].ToString();
_notificationlevel = systeminfo["Logging:LogLevel:Notify"].ToString();
_swagger = systeminfo["UseSwagger"].ToString();
_swagger = systeminfo["UseSwagger"].ToString();
_cachecontrol = systeminfo["CacheControl"].ToString();
_packageregistryurl = systeminfo["PackageRegistryUrl"].ToString();
}
_packageregistryemail = systeminfo["PackageRegistryEmail"].ToString();
}
systeminfo = await SystemService.GetSystemInfoAsync("log");
if (systeminfo != null)
@ -229,8 +247,10 @@
settings.Add("Logging:LogLevel:Default", _logginglevel);
settings.Add("Logging:LogLevel:Notify", _notificationlevel);
settings.Add("UseSwagger", _swagger);
settings.Add("PackageRegistryUrl", _packageregistryurl);
await SystemService.UpdateSystemInfoAsync(settings);
settings.Add("CacheControl", _cachecontrol);
settings.Add("PackageRegistryUrl", _packageregistryurl);
settings.Add("PackageRegistryEmail", _packageregistryemail);
await SystemService.UpdateSystemInfoAsync(settings);
AddModuleMessage(Localizer["Success.UpdateConfig.Restart"], MessageType.Success);
}
catch (Exception ex)

View File

@ -125,7 +125,7 @@
<TabPanel Name="Upload" ResourceKey="Upload">
<div class="container">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" HelpText="Upload one or more theme packages. Once they are uploaded click Install to complete the installation." ResourceKey="Theme">Theme: </Label>
<Label Class="col-sm-3" HelpText="Upload one or more theme packages." ResourceKey="Theme">Theme: </Label>
<div class="col-sm-9">
<FileManager Folder="@Constants.PackagesFolder" UploadMultiple="true" OnUpload="OnUpload" />
</div>

View File

@ -45,24 +45,7 @@
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="packagename" HelpText="The unique name of the package from which this theme was installed. This value must be specified within the theme's ITheme interface specification." ResourceKey="PackageName">Package Name: </Label>
<div class="col-sm-9">
@if (!string.IsNullOrEmpty(_packagename))
{
<div class="input-group">
<input id="packagename" class="form-control" @bind="@_packagename" disabled />
@if (string.IsNullOrEmpty(_packageurl))
{
<button type="button" class="btn btn-secondary" @onclick="ValidatePackage">@Localizer["Validate"]</button>
}
else
{
<a href="@_packageurl" target="_blank" class="btn btn-primary">@SharedLocalizer["Download"]</a>
}
</div>
}
else
{
<input id="packagename" class="form-control" @bind="@_packagename" disabled />
}
<input id="packagename" class="form-control" @bind="@_packagename" disabled />
</div>
</div>
<div class="row mb-1 align-items-center">
@ -116,7 +99,6 @@
private string _name;
private string _version;
private string _packagename = "";
private string _packageurl = "";
private string _owner = "";
private string _url = "";
private string _contact = "";
@ -185,27 +167,4 @@
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
}
}
private async Task ValidatePackage()
{
try
{
var package = await PackageService.GetPackageAsync(_packagename, _version, true);
if (package == null || string.IsNullOrEmpty(package.PackageUrl))
{
AddModuleMessage(Localizer["Message.Validate"], MessageType.Warning);
}
else
{
_packageurl = package.PackageUrl;
AddModuleMessage(Localizer["Message.Download"], MessageType.Info);
}
StateHasChanged();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Downloading Package {PackageId} {Version}", _packagename, _version);
AddModuleMessage(Localizer["Error.Validate"], MessageType.Error);
}
}
}

View File

@ -4,6 +4,7 @@
@inject NavigationManager NavigationManager
@inject IThemeService ThemeService
@inject IPackageService PackageService
@inject ISiteService SiteService
@inject IStringLocalizer<Index> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer
@ -19,6 +20,7 @@ else
<Pager Items="@_themes">
<Header>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>@SharedLocalizer["Name"]</th>
@ -32,10 +34,11 @@ else
<td><ActionLink Action="Edit" Text="Edit" Parameters="@($"id=" + context.ThemeId.ToString())" ResourceKey="EditTheme" /></td>
<td>
@if (context.AssemblyName != Constants.ClientId)
{
{
<ActionDialog Header="Delete Theme" Message="@string.Format(Localizer["Confirm.Theme.Delete"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Host" Class="btn btn-danger" OnClick="@(async () => await DeleteTheme(context))" ResourceKey="DeleteTheme" />
}
}
</td>
<td><NavLink class="btn btn-secondary" href="@NavigateUrl("admin/site")">@Localizer["Assign"]</NavLink></td>
<td>@context.Name</td>
<td>@context.Version</td>
<td>

View File

@ -54,6 +54,8 @@
}
else
{
AddModuleMessage(Localizer["Disclaimer.Text"], MessageType.Warning);
List<Package> packages = await PackageService.GetPackagesAsync("framework", "", "", "");
if (packages != null)
{
@ -97,13 +99,16 @@
{
try
{
ShowProgressIndicator();
await PackageService.DownloadPackageAsync(packageid, version);
await PackageService.DownloadPackageAsync(Constants.UpdaterPackageId, version);
HideProgressIndicator();
AddModuleMessage(Localizer["Success.Framework.Download"], MessageType.Success);
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Downloading Framework Package {Error}", ex.Message);
HideProgressIndicator();
AddModuleMessage(Localizer["Error.Framework.Download"], MessageType.Error);
}
}

View File

@ -3,6 +3,7 @@
@inject NavigationManager NavigationManager
@inject IUrlMappingService UrlMappingService
@inject ISiteService SiteService
@inject ISettingService SettingService
@inject IStringLocalizer<Index> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer
@ -62,7 +63,13 @@ else
</select>
</div>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="retention" HelpText="Number of days of broken urls to retain" ResourceKey="Retention">Retention (Days): </Label>
<div class="col-sm-9">
<input id="retention" class="form-control" type="number" min="0" step="1" @bind="@_retention" />
</div>
</div>
</div>
<br />
<button type="button" class="btn btn-success" @onclick="SaveSiteSettings">@SharedLocalizer["Save"]</button>
</TabPanel>
@ -73,6 +80,7 @@ else
private bool _mapped = true;
private List<UrlMapping> _urlMappings;
private string _capturebrokenurls;
private int _retention = 30;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
@ -80,7 +88,10 @@ else
{
await GetUrlMappings();
_capturebrokenurls = PageState.Site.CaptureBrokenUrls.ToString();
}
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
_retention = int.Parse(SettingService.GetSetting(settings, "UrlMappingRetention", "30"));
}
private async void MappedChanged(ChangeEventArgs e)
{
@ -124,7 +135,12 @@ else
var site = PageState.Site;
site.CaptureBrokenUrls = bool.Parse(_capturebrokenurls);
await SiteService.UpdateSiteAsync(site);
AddModuleMessage(Localizer["Success.SaveSiteSettings"], MessageType.Success);
var settings = await SettingService.GetSiteSettingsAsync(site.SiteId);
settings = SettingService.SetSetting(settings, "UrlMappingRetention", _retention.ToString(), true);
await SettingService.UpdateSiteSettingsAsync(settings, site.SiteId);
AddModuleMessage(Localizer["Success.SaveSiteSettings"], MessageType.Success);
}
catch (Exception ex)
{

View File

@ -9,6 +9,8 @@
@inject INotificationService NotificationService
@inject IFileService FileService
@inject IFolderService FolderService
@inject IJSRuntime jsRuntime
@inject IServiceProvider ServiceProvider
@inject IStringLocalizer<Index> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer
@ -84,6 +86,7 @@
<br />
<button type="button" class="btn btn-success" @onclick="Save">@SharedLocalizer["Save"]</button>
<button type="button" class="btn btn-secondary" @onclick="Cancel">@SharedLocalizer["Cancel"]</button>
<button type="button" class="btn btn-danger" @onclick="Logout">@Localizer["Logout Everywhere"]</button>
</TabPanel>
<TabPanel Name="Profile" ResourceKey="Profile">
<div class="container">
@ -102,7 +105,7 @@
}
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="@p.Name" HelpText="@p.Description">@p.Title</Label>
<div class="col-sm-9">
<div class="col-sm-9">
@if (!string.IsNullOrEmpty(p.Options))
{
@if (!string.IsNullOrEmpty(p.Autocomplete))
@ -146,22 +149,26 @@
{
@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))" autocomplete="@p.Autocomplete" />
<input id="@p.Name" class="form-control" value="@GetProfileValue(p.Name, p.DefaultValue)" required @onchange="@(e => ProfileChanged(e, p.Name))" autocomplete="@p.Autocomplete"
@attributes="@(p.MaxLength > 0 ? new Dictionary<string, object> {{"maxlength", p.MaxLength }} : null)" />
}
else
{
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" autocomplete="@p.Autocomplete" />
<input id="@p.Name" class="form-control" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" autocomplete="@p.Autocomplete"
@attributes="@(p.MaxLength > 0 ? new Dictionary<string, object> {{"maxlength", p.MaxLength }} : null)" />
}
}
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))" />
<input id="@p.Name" class="form-control" value="@GetProfileValue(p.Name, p.DefaultValue)" required @onchange="@(e => ProfileChanged(e, p.Name))"
@attributes="@(p.MaxLength > 0 ? new Dictionary<string, object> {{"maxlength", p.MaxLength }} : null)" />
}
else
{
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" />
<input id="@p.Name" class="form-control" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))"
@attributes="@(p.MaxLength > 0 ? new Dictionary<string, object> {{"maxlength", p.MaxLength }} : null)" />
}
}
}
@ -171,22 +178,26 @@
{
@if (p.IsRequired)
{
<textarea id="@p.Name" class="form-control" maxlength="@p.MaxLength" rows="@p.Rows" value="@GetProfileValue(p.Name, p.DefaultValue)" required @onchange="@(e => ProfileChanged(e, p.Name))" autocomplete="@p.Autocomplete"></textarea>
<textarea id="@p.Name" class="form-control" rows="@p.Rows" value="@GetProfileValue(p.Name, p.DefaultValue)" required @onchange="@(e => ProfileChanged(e, p.Name))" autocomplete="@p.Autocomplete"
@attributes="@(p.MaxLength > 0 ? new Dictionary<string, object> {{"maxlength", p.MaxLength }} : null)"></textarea>
}
else
{
<textarea id="@p.Name" class="form-control" maxlength="@p.MaxLength" rows="@p.Rows" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" autocomplete="@p.Autocomplete"></textarea>
<textarea id="@p.Name" class="form-control" rows="@p.Rows" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" autocomplete="@p.Autocomplete"
@attributes="@(p.MaxLength > 0 ? new Dictionary<string, object> {{"maxlength", p.MaxLength }} : null)"></textarea>
}
}
else
{
@if (p.IsRequired)
{
<textarea id="@p.Name" class="form-control" maxlength="@p.MaxLength" rows="@p.Rows" value="@GetProfileValue(p.Name, p.DefaultValue)" required @onchange="@(e => ProfileChanged(e, p.Name))"></textarea>
<textarea id="@p.Name" class="form-control" rows="@p.Rows" value="@GetProfileValue(p.Name, p.DefaultValue)" required @onchange="@(e => ProfileChanged(e, p.Name))"
@attributes="@(p.MaxLength > 0 ? new Dictionary<string, object> {{"maxlength", p.MaxLength }} : null)"></textarea>
}
else
{
<textarea id="@p.Name" class="form-control" maxlength="@p.MaxLength" rows="@p.Rows" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))"></textarea>
<textarea id="@p.Name" class="form-control" rows="@p.Rows" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))"
@attributes="@(p.MaxLength > 0 ? new Dictionary<string, object> {{"maxlength", p.MaxLength }} : null)"></textarea>
}
}
}
@ -215,11 +226,11 @@
{
<Pager Items="@notifications">
<Header>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>@Localizer["From"]</th>
<th>@Localizer["Subject"]</th>
<th>@Localizer["Received"]</th>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</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" ResourceKey="ViewNotification" ReturnUrl="@NavigateUrl(PageState.Page.Path, "tab=Notifications")" /></td>
@ -227,13 +238,13 @@
@if (context.IsRead)
{
<td>@context.FromDisplayName</td>
<td>@(string.IsNullOrEmpty(context.FromDisplayName) ? SharedLocalizer["System"] : context.FromDisplayName)</td>
<td>@context.Subject</td>
<td>@string.Format("{0:dd-MMM-yyyy HH:mm:ss}", @context.CreatedOn)</td>
}
else
{
<td><b>@context.FromDisplayName</b></td>
<td><b>@(string.IsNullOrEmpty(context.FromDisplayName) ? SharedLocalizer["System"] : context.FromDisplayName)</b></td>
<td><b>@context.Subject</b></td>
<td><b>@string.Format("{0:dd-MMM-yyyy HH:mm:ss}", @context.CreatedOn)</b></td>
}
@ -278,11 +289,11 @@
{
<Pager Items="@notifications">
<Header>
<th style="width: 1px;"></th>
<th style="width: 1px;"></th>
<th>@Localizer["To"]</th>
<th>@Localizer["Subject"]</th>
<th>@Localizer["Sent"]</th>
<th style="width: 1px;"></th>
<th style="width: 1px;"></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" ResourceKey="ViewNotification" ReturnUrl="@NavigateUrl(PageState.Page.Path, "tab=Notifications")" /></td>
@ -360,7 +371,7 @@
private File photo = null;
private string _ImageFiles = string.Empty;
private List<Profile> profiles;
private Dictionary<string, string> settings;
private Dictionary<string, string> userSettings;
private string category = string.Empty;
private string filter = "to";
@ -408,9 +419,9 @@
photo = null;
}
settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId);
userSettings = PageState.User.Settings;
var sitesettings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
_ImageFiles = SettingService.GetSetting(settings, "ImageFiles", Constants.ImageFiles);
_ImageFiles = SettingService.GetSetting(userSettings, "ImageFiles", Constants.ImageFiles);
_ImageFiles = (string.IsNullOrEmpty(_ImageFiles)) ? Constants.ImageFiles : _ImageFiles;
await LoadNotificationsAsync();
@ -437,7 +448,7 @@
private string GetProfileValue(string SettingName, string DefaultValue)
{
string value = SettingService.GetSetting(settings, SettingName, DefaultValue);
string value = SettingService.GetSetting(userSettings, SettingName, DefaultValue);
if (value.Contains("]"))
{
value = value.Substring(value.IndexOf("]") + 1);
@ -480,7 +491,7 @@
user = await UserService.UpdateUserAsync(user);
if (user != null)
{
await SettingService.UpdateUserSettingsAsync(settings, PageState.User.UserId);
await SettingService.UpdateUserSettingsAsync(userSettings, PageState.User.UserId);
await logger.LogInformation("User Profile Saved");
if (!string.IsNullOrEmpty(PageState.ReturnUrl))
@ -518,6 +529,32 @@
}
}
private async Task Logout()
{
await logger.LogInformation("User Logout Everywhere For Username {Username}", PageState.User?.Username);
var url = NavigateUrl(""); // home page
if (PageState.Runtime == Shared.Runtime.Hybrid)
{
if (PageState.User != null)
{
// hybrid apps utilize an interactive logout
await UserService.LogoutUserEverywhereAsync(PageState.User);
var authstateprovider = (IdentityAuthenticationStateProvider)ServiceProvider.GetService(typeof(IdentityAuthenticationStateProvider));
authstateprovider.NotifyAuthenticationChanged();
NavigationManager.NavigateTo(url, true);
}
}
else
{
// post to the Logout page to complete the logout process
var fields = new { __RequestVerificationToken = SiteState.AntiForgeryToken, returnurl = url, everywhere = true };
var interop = new Interop(jsRuntime);
await interop.SubmitForm(Utilities.TenantUrl(PageState.Alias, "/pages/logout/"), fields);
}
}
private bool ValidateProfiles()
{
foreach (Profile profile in profiles)
@ -525,7 +562,7 @@
var value = GetProfileValue(profile.Name, string.Empty);
if (string.IsNullOrEmpty(value) && !string.IsNullOrEmpty(profile.DefaultValue))
{
settings = SettingService.SetSetting(settings, profile.Name, profile.DefaultValue);
userSettings = SettingService.SetSetting(userSettings, profile.Name, profile.DefaultValue);
}
if (!profile.IsPrivate || UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
{
@ -557,7 +594,7 @@
private void ProfileChanged(ChangeEventArgs e, string SettingName)
{
var value = (string)e.Value;
settings = SettingService.SetSetting(settings, SettingName, value);
userSettings = SettingService.SetSetting(userSettings, SettingName, value);
}
private async Task Delete(Notification Notification)

View File

@ -128,7 +128,7 @@
createdon = notification.CreatedOn.ToString();
body = notification.Body;
if (title == "From")
if (title == "From" && !notification.IsRead)
{
notification.IsRead = true;
notification = await NotificationService.UpdateNotificationAsync(notification);

View File

@ -14,7 +14,6 @@
<TabPanel Name="Identity" ResourceKey="Identity">
@if (profiles != null)
{
<ModuleMessage Message="@_passwordrequirements" Type="MessageType.Info" />
<div class="container">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="username" HelpText="A unique username for a user. Note that this field can not be modified once it is saved." ResourceKey="Username"></Label>
@ -22,24 +21,6 @@
<input id="username" class="form-control" @bind="@_username" />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="password" HelpText="The user's password. Please choose a password which is sufficiently secure." ResourceKey="Password"></Label>
<div class="col-sm-9">
<div class="input-group">
<input id="password" type="@_passwordtype" class="form-control" @bind="@_password" autocomplete="new-password" required />
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword" tabindex="-1">@_togglepassword</button>
</div>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="confirm" HelpText="Please enter the password again to confirm it matches with the value above" ResourceKey="Confirm"></Label>
<div class="col-sm-9">
<div class="input-group">
<input id="confirm" type="@_passwordtype" class="form-control" @bind="@_confirm" autocomplete="new-password" required />
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword" tabindex="-1">@_togglepassword</button>
</div>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="email" HelpText="The email address where the user will receive notifications" ResourceKey="Email"></Label>
<div class="col-sm-9">
@ -100,11 +81,11 @@
{
@if (p.Rows == 1)
{
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" />
<input id="@p.Name" class="form-control" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" @attributes="@(p.MaxLength > 0 ? new Dictionary<string, object> {{"maxlength", p.MaxLength }} : null)" />
}
else
{
<textarea id="@p.Name" class="form-control" maxlength="@p.MaxLength" rows="@p.Rows" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))"></textarea>
<textarea id="@p.Name" class="form-control" rows="@p.Rows" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" @attributes="@(p.MaxLength > 0 ? new Dictionary<string, object> {{"maxlength", p.MaxLength }} : null)"></textarea>
}
}
</div>
@ -123,12 +104,7 @@
@code {
private bool _initialized = false;
private string _passwordrequirements;
private string _username = string.Empty;
private string _password = string.Empty;
private string _passwordtype = "password";
private string _togglepassword = string.Empty;
private string _confirm = string.Empty;
private string _email = string.Empty;
private string _displayname = string.Empty;
private string _notify = "True";
@ -142,8 +118,6 @@
{
try
{
_passwordrequirements = await UserService.GetPasswordRequirementsAsync(PageState.Site.SiteId);
_togglepassword = SharedLocalizer["ShowPassword"];
profiles = await ProfileService.GetProfilesAsync(ModuleState.SiteId);
settings = new Dictionary<string, string>();
_initialized = true;
@ -169,39 +143,32 @@
{
try
{
if (_username != string.Empty && _password != string.Empty && _confirm != string.Empty && _email != string.Empty)
if (_username != string.Empty && _email != string.Empty)
{
if (_password == _confirm)
if (ValidateProfiles())
{
if (ValidateProfiles())
var user = new User();
user.SiteId = PageState.Site.SiteId;
user.Username = _username;
user.Password = ""; // will be auto generated
user.Email = _email;
user.DisplayName = string.IsNullOrWhiteSpace(_displayname) ? _username : _displayname;
user.PhotoFileId = null;
user.SuppressNotification = !bool.Parse(_notify);
user = await UserService.AddUserAsync(user);
if (user != null)
{
var user = new User();
user.SiteId = PageState.Site.SiteId;
user.Username = _username;
user.Password = _password;
user.Email = _email;
user.DisplayName = string.IsNullOrWhiteSpace(_displayname) ? _username : _displayname;
user.PhotoFileId = null;
user.SuppressNotification = !bool.Parse(_notify);
user = await UserService.AddUserAsync(user);
if (user != null)
{
await SettingService.UpdateUserSettingsAsync(settings, user.UserId);
await logger.LogInformation("User Created {User}", user);
NavigationManager.NavigateTo(NavigateUrl());
}
else
{
await logger.LogError("Error Adding User {Username} {Email}", _username, _email);
AddModuleMessage(Localizer["Error.User.AddCheckPass"], MessageType.Error);
}
await SettingService.UpdateUserSettingsAsync(settings, user.UserId);
await logger.LogInformation("User Created {User}", user);
NavigationManager.NavigateTo(NavigateUrl());
}
else
{
await logger.LogError("Error Adding User {Username} {Email}", _username, _email);
AddModuleMessage(Localizer["Error.User.AddCheckPass"], MessageType.Error);
}
}
else
{
AddModuleMessage(Localizer["Message.Password.NoMatch"], MessageType.Warning);
}
}
else
@ -252,18 +219,4 @@
var value = (string)e.Value;
settings = SettingService.SetSetting(settings, SettingName, value);
}
private void TogglePassword()
{
if (_passwordtype == "password")
{
_passwordtype = "text";
_togglepassword = SharedLocalizer["HidePassword"];
}
else
{
_passwordtype = "password";
_togglepassword = SharedLocalizer["ShowPassword"];
}
}
}

View File

@ -6,6 +6,7 @@
@inject IProfileService ProfileService
@inject ISettingService SettingService
@inject IFileService FileService
@inject IServiceProvider ServiceProvider
@inject IStringLocalizer<Edit> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer
@ -51,15 +52,18 @@
<input id="displayname" class="form-control" @bind="@displayname" />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="isdeleted" HelpText="Indicate if the user is active" ResourceKey="IsDeleted"></Label>
<div class="col-sm-9">
<select id="isdeleted" class="form-select" @bind="@isdeleted">
<option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option>
</select>
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
{
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="isdeleted" HelpText="Indicate if the user is active" ResourceKey="IsDeleted"></Label>
<div class="col-sm-9">
<select id="isdeleted" class="form-select" @bind="@isdeleted">
<option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option>
</select>
</div>
</div>
</div>
}
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="lastlogin" HelpText="The date and time when the user last signed in" ResourceKey="LastLogin"></Label>
<div class="col-sm-9">
@ -89,8 +93,8 @@
}
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="@p.Name" HelpText="@p.Description">@p.Title</Label>
<div class="col-sm-9">
@if (!string.IsNullOrEmpty(p.Options))
<div class="col-sm-9">
@if (!string.IsNullOrEmpty(p.Options))
{
<select id="@p.Name" class="form-select" @onchange="@(e => ProfileChanged(e, p.Name))">
@foreach (var option in p.Options.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
@ -110,11 +114,11 @@
{
@if (p.Rows == 1)
{
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" />
<input id="@p.Name" class="form-control" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" @attributes="@(p.MaxLength > 0 ? new Dictionary<string, object> {{"maxlength", p.MaxLength }} : null)" />
}
else
{
<textarea id="@p.Name" class="form-control" maxlength="@p.MaxLength" rows="@p.Rows" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))"></textarea>
<textarea id="@p.Name" class="form-control" rows="@p.Rows" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" @attributes="@(p.MaxLength > 0 ? new Dictionary<string, object> {{"maxlength", p.MaxLength }} : null)"></textarea>
}
}
</div>
@ -127,12 +131,19 @@
<button type="button" class="btn btn-success" @onclick="SaveUser">@SharedLocalizer["Save"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
<br />
<br />
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin) && PageState.Runtime != Shared.Runtime.Hybrid && !ishost)
{
<button type="button" class="btn btn-primary ms-1" @onclick="ImpersonateUser">@Localizer["Impersonate"]</button>
}
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host) && isdeleted == "True")
{
<ActionDialog Header="Delete User" Message="Are You Sure You Wish To Permanently Delete This User?" Action="Delete" Security="SecurityAccessLevel.Host" Class="btn btn-danger" OnClick="@(async () => await DeleteUser())" ResourceKey="DeleteUser" />
}
<br /><br />
<AuditInfo CreatedBy="@createdby" CreatedOn="@createdon" ModifiedBy="@modifiedby" ModifiedOn="@modifiedon" DeletedBy="@deletedby" DeletedOn="@deletedon"></AuditInfo>
}
@code {
@code {
private bool _initialized = false;
private string _passwordrequirements;
private int userid;
@ -146,9 +157,10 @@
private string isdeleted;
private string lastlogin;
private string lastipaddress;
private bool ishost = false;
private List<Profile> profiles;
private Dictionary<string, string> settings;
private Dictionary<string, string> userSettings;
private string category = string.Empty;
private string createdby;
@ -180,8 +192,9 @@
isdeleted = user.IsDeleted.ToString();
lastlogin = string.Format("{0:MMM dd yyyy HH:mm:ss}", user.LastLoginOn);
lastipaddress = user.LastIPAddress;
ishost = UserSecurity.ContainsRole(user.Roles, RoleNames.Host);
settings = await SettingService.GetUserSettingsAsync(user.UserId);
userSettings = user.Settings;
createdby = user.CreatedBy;
createdon = user.CreatedOn;
modifiedby = user.ModifiedBy;
@ -202,7 +215,7 @@
private string GetProfileValue(string SettingName, string DefaultValue)
{
string value = SettingService.GetSetting(settings, SettingName, DefaultValue);
string value = SettingService.GetSetting(userSettings, SettingName, DefaultValue);
if (value.Contains("]"))
{
value = value.Substring(value.IndexOf("]") + 1);
@ -226,13 +239,15 @@
user.Password = _password;
user.Email = email;
user.DisplayName = string.IsNullOrWhiteSpace(displayname) ? username : displayname;
user.IsDeleted = (isdeleted == null ? true : Boolean.Parse(isdeleted));
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
{
user.IsDeleted = (isdeleted == null ? true : Boolean.Parse(isdeleted));
}
user = await UserService.UpdateUserAsync(user);
if (user != null)
{
await SettingService.UpdateUserSettingsAsync(settings, user.UserId);
await SettingService.UpdateUserSettingsAsync(userSettings, user.UserId);
await logger.LogInformation("User Saved {User}", user);
NavigationManager.NavigateTo(NavigateUrl());
}
@ -259,6 +274,44 @@
}
}
private async Task ImpersonateUser()
{
try
{
await logger.LogInformation(LogFunction.Security, "User {Username} Impersonated By Administrator {Administrator}", username, PageState.User.Username);
// post back to the server so that the cookies are set correctly
var interop = new Interop(JSRuntime);
var fields = new { __RequestVerificationToken = SiteState.AntiForgeryToken, username = username, returnurl = PageState.Alias.Path };
string url = Utilities.TenantUrl(PageState.Alias, "/pages/impersonate/");
await interop.SubmitForm(url, fields);
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Impersonating User {Username} {Error}", username, ex.Message);
AddModuleMessage(Localizer["Error.User.Impersonate"], MessageType.Error);
}
}
private async Task DeleteUser()
{
try
{
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host) && userid != PageState.User.UserId)
{
var user = await UserService.GetUserAsync(userid, PageState.Site.SiteId);
await UserService.DeleteUserAsync(user.UserId, PageState.Site.SiteId);
await logger.LogInformation("User Permanently Deleted {User}", user);
NavigationManager.NavigateTo(NavigateUrl());
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Permanently Deleting User {UserId} {Error}", userid, ex.Message);
AddModuleMessage(Localizer["Error.DeleteUser"], MessageType.Error);
}
}
private bool ValidateProfiles()
{
foreach (Profile profile in profiles)
@ -266,7 +319,7 @@
var value = GetProfileValue(profile.Name, string.Empty);
if (string.IsNullOrEmpty(value) && !string.IsNullOrEmpty(profile.DefaultValue))
{
settings = SettingService.SetSetting(settings, profile.Name, profile.DefaultValue);
userSettings = SettingService.SetSetting(userSettings, profile.Name, profile.DefaultValue);
}
if (!profile.IsPrivate || UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
{
@ -293,7 +346,7 @@
private void ProfileChanged(ChangeEventArgs e, string SettingName)
{
var value = (string)e.Value;
settings = SettingService.SetSetting(settings, SettingName, value);
userSettings = SettingService.SetSetting(userSettings, SettingName, value);
}
private void TogglePassword()

File diff suppressed because it is too large Load Diff

View File

@ -53,17 +53,17 @@ else
<p align="center">
<Pager Items="@userroles">
<Header>
<th>@Localizer["Roles"]</th>
<th>@Localizer["Effective"]</th>
<th>@Localizer["Expiry"]</th>
<th>&nbsp;</th>
<th>@Localizer["Roles"]</th>
<th>@Localizer["Effective"]</th>
<th>@Localizer["Expiry"]</th>
<th>&nbsp;</th>
</Header>
<Row>
<td>@context.Role.Name</td>
<td>@Utilities.UtcAsLocalDate(context.EffectiveDate)</td>
<td>@Utilities.UtcAsLocalDate(context.ExpiryDate)</td>
<td>
<ActionDialog Header="Remove Role" Message="@string.Format(Localizer["Confirm.User.RemoveRole"], context.Role.Name)" Action="Delete" Security="SecurityAccessLevel.Edit" Class="btn btn-danger" OnClick="@(async () => await DeleteUserRole(context.UserRoleId))" Disabled="@(context.Role.IsAutoAssigned || (context.Role.Name == RoleNames.Host && userid == PageState.User.UserId))" ResourceKey="DeleteUserRole" />
<ActionDialog Header="Remove Role" Message="@string.Format(Localizer["Confirm.User.RemoveRole"], context.Role.Name)" Action="Delete" Security="SecurityAccessLevel.Edit" Class="btn btn-danger" OnClick="@(async () => await DeleteUserRole(context.UserRoleId))" Disabled="@(context.Role.Name == RoleNames.Host && userid == PageState.User.UserId)" ResourceKey="DeleteUserRole" />
</td>
</Row>
</Pager>
@ -171,8 +171,18 @@ else
{
try
{
await UserRoleService.DeleteUserRoleAsync(UserRoleId);
await logger.LogInformation("User Removed From Role {UserRoleId}", UserRoleId);
var userrole = await UserRoleService.GetUserRoleAsync(UserRoleId);
if (userrole.Role.Name == RoleNames.Registered)
{
userrole.ExpiryDate = DateTime.UtcNow;
await UserRoleService.UpdateUserRoleAsync(userrole);
await logger.LogInformation("User {Username} Expired From Role {Role}", userrole.User.Username, userrole.Role.Name);
}
else
{
await UserRoleService.DeleteUserRoleAsync(UserRoleId);
await logger.LogInformation("User {Username} Removed From Role {Role}", userrole.User.Username, userrole.Role.Name);
}
AddModuleMessage(Localizer["Success.User.Remove"], MessageType.Success);
await GetUserRoles();
StateHasChanged();

View File

@ -22,9 +22,9 @@
<div class="modal-footer">
@if (!string.IsNullOrEmpty(Action))
{
<button type="button" class="@Class" @onclick="Confirm">@((MarkupString)_iconSpan) @Text</button>
<button type="button" class="@ConfirmClass" @onclick="Confirm">@((MarkupString)_iconSpan) @Text</button>
}
<button type="button" class="btn btn-secondary" @onclick="DisplayModal">@SharedLocalizer["Cancel"]</button>
<button type="button" class="@CancelClass" @onclick="DisplayModal">@SharedLocalizer["Cancel"]</button>
</div>
</div>
</div>
@ -51,27 +51,27 @@ else
<div class="modal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">@Header</h5>
<form method="post" @formname="@($"ActionDialogCloseForm{Id}")" @onsubmit="DisplayModal" data-enhance>
<input type="hidden" name="@Constants.RequestVerificationToken" value="@SiteState.AntiForgeryToken" />
<form class="app-form-inline" method="post" @formname="@($"ActionDialogCloseForm:{ModuleState.PageModuleId}:{Id}")" @onsubmit="DisplayModal" data-enhance>
<input type="hidden" name="@Constants.RequestVerificationToken" value="@SiteState.AntiForgeryToken" />
<div class="modal-header">
<h5 class="modal-title">@Header</h5>
<button type="submit" class="btn-close" aria-label="Close"></button>
</form>
</div>
</div>
</form>
<div class="modal-body">
<p>@Message</p>
</div>
<div class="modal-footer">
@if (!string.IsNullOrEmpty(Action))
{
<form method="post" @formname="@($"ActionDialogConfirmForm{Id}")" @onsubmit="Confirm" data-enhance>
<form method="post" @formname="@($"ActionDialogConfirmForm:{ModuleState.PageModuleId}:{Id}")" @onsubmit="Confirm" data-enhance>
<input type="hidden" name="@Constants.RequestVerificationToken" value="@SiteState.AntiForgeryToken" />
<button type="submit" class="@Class">@((MarkupString)_iconSpan) @Text</button>
<button type="submit" class="@ConfirmClass">@((MarkupString)_iconSpan) @Text</button>
</form>
}
<form method="post" @formname="@($"ActionDialogCancelForm{Id}")" @onsubmit="DisplayModal" data-enhance>
<form method="post" @formname="@($"ActionDialogCancelForm:{ModuleState.PageModuleId}:{Id}")" @onsubmit="DisplayModal" data-enhance>
<input type="hidden" name="@Constants.RequestVerificationToken" value="@SiteState.AntiForgeryToken" />
<button type="submit" class="btn btn-secondary">@SharedLocalizer["Cancel"]</button>
<button type="submit" class="@CancelClass">@SharedLocalizer["Cancel"]</button>
</form>
</div>
</div>
@ -87,7 +87,7 @@ else
}
else
{
<form method="post" @formname="@($"ActionDialogActionForm{Id}")" @onsubmit="DisplayModal" data-enhance>
<form method="post" class="app-form-inline" @formname="@($"ActionDialogActionForm:{ModuleState.PageModuleId}:{Id}")" @onsubmit="DisplayModal" data-enhance>
<input type="hidden" name="@Constants.RequestVerificationToken" value="@SiteState.AntiForgeryToken" />
<button type="submit" class="@Class">@((MarkupString)_openIconSpan) @_openText</button>
</form>
@ -128,6 +128,12 @@ else
[Parameter]
public string Class { get; set; } // optional
[Parameter]
public string ConfirmClass { get; set; } // optional - for Confirm modal button
[Parameter]
public string CancelClass { get; set; } // optional - for Cancel modal button
[Parameter]
public bool Disabled { get; set; } // optional
@ -162,18 +168,33 @@ else
{
Text = Action;
}
_openText = Text;
if (string.IsNullOrEmpty(Class))
{
Class = "btn btn-success";
}
if (string.IsNullOrEmpty(ConfirmClass))
{
ConfirmClass = Class;
}
if (string.IsNullOrEmpty(CancelClass))
{
CancelClass = "btn btn-secondary";
}
if (!string.IsNullOrEmpty(EditMode))
{
_editmode = bool.Parse(EditMode);
}
Text = Localize(nameof(Text), Text);
Header = Localize(nameof(Header), Header);
Message = Localize(nameof(Message), Message);
_openText = Text;
if (!string.IsNullOrEmpty(IconName))
{
if (IconOnly)
@ -181,7 +202,10 @@ else
_openText = string.Empty;
}
if (!IconName.Contains(" "))
// Check if IconName starts with "oi oi-"
bool startsWithOiOi = IconName.StartsWith("oi oi-");
if (!startsWithOiOi && !IconName.Contains(" "))
{
IconName = "oi oi-" + IconName;
}
@ -189,13 +213,11 @@ else
_iconSpan = $"<span class=\"{IconName}\"></span>&nbsp";
}
Text = Localize(nameof(Text), Text);
Header = Localize(nameof(Header), Header);
Message = Localize(nameof(Message), Message);
_permissions = (PermissionList == null) ? ModuleState.PermissionList : PermissionList;
_authorized = IsAuthorized();
if (string.IsNullOrEmpty(Id)) Id = "1";
if (PageState.QueryString.ContainsKey("dialog"))
{
_visible = (PageState.QueryString["dialog"] == Id);

View File

@ -145,7 +145,10 @@
if (!string.IsNullOrEmpty(IconName))
{
if (!IconName.Contains(" "))
// Check if IconName starts with "oi oi-"
bool startsWithOiOi = IconName.StartsWith("oi oi-");
if (!startsWithOiOi && !IconName.Contains(" "))
{
IconName = "oi oi-" + IconName;
}

View File

@ -3,8 +3,8 @@
@inherits ModuleControlBase
@inject IFolderService FolderService
@inject IFileService FileService
@inject ISettingService SettingService
@inject IUserService UserService
@inject ISettingService SettingService
@inject IStringLocalizer<FileManager> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer
@ -157,6 +157,9 @@
[Parameter]
public bool UploadMultiple { get; set; } = false; // optional - enable multiple file uploads - default false
[Parameter]
public int ChunkSize { get; set; } = 1; // optional - size of file chunks to upload in MB
[Parameter]
public EventCallback<int> OnUpload { get; set; } // optional - executes a method in the calling component when a file is uploaded
@ -196,7 +199,7 @@
else
{
FolderId = -1;
_message = "Folder Path " + Folder + "Does Not Exist";
_message = "Folder Path " + Folder + " Does Not Exist";
_messagetype = MessageType.Error;
}
}
@ -226,9 +229,9 @@
}
else
{
FileId = -1; // file does not exist
_message = "FileId " + FileId.ToString() + "Does Not Exist";
_message = "FileId " + FileId.ToString() + " Does Not Exist";
_messagetype = MessageType.Error;
FileId = -1; // file does not exist
}
}
@ -314,7 +317,6 @@
await SetImage();
await OnSelect.InvokeAsync(FileId);
StateHasChanged();
}
private async Task SetImage()
@ -360,11 +362,7 @@
}
if (restricted == "")
{
if (!ShowProgress)
{
_uploading = true;
StateHasChanged();
}
CancellationTokenSource tokenSource = new CancellationTokenSource();
try
{
@ -375,53 +373,31 @@
if (PageState.Runtime == Shared.Runtime.Hybrid)
{
jwt = await UserService.GetTokenAsync();
if (string.IsNullOrEmpty(jwt))
{
await logger.LogInformation("File Upload Failed From .NET MAUI Due To Missing Security Token. Token Options Must Be Set In User Settings.");
_message = "Security Token Not Specified";
_messagetype = MessageType.Error;
return;
}
}
await interop.UploadFiles(posturl, folder, _guid, SiteState.AntiForgeryToken, jwt);
// uploading is asynchronous so we need to poll to determine if uploads are completed
var success = true;
int upload = 0;
while (upload < uploads.Length && success)
var chunksize = ChunkSize;
if (chunksize == 1)
{
success = false;
var filename = uploads[upload].Split(':')[0];
var size = Int64.Parse(uploads[upload].Split(':')[1]); // bytes
var megabits = (size / 1048576.0) * 8; // binary conversion
var uploadspeed = 2; // 2 Mbps (3G ranges from 300Kbps to 3Mbps)
var uploadtime = (megabits / uploadspeed); // seconds
var maxattempts = 5; // polling (minimum timeout duration will be 5 seconds)
var sleep = (int)Math.Ceiling(uploadtime / maxattempts) * 1000; // milliseconds
int attempts = 0;
while (attempts < maxattempts && !success)
{
attempts += 1;
Thread.Sleep(sleep);
if (Folder == Constants.PackagesFolder)
{
var files = await FileService.GetFilesAsync(folder);
if (files != null && files.Any(item => item.Name == filename))
{
success = true;
}
}
else
{
var file = await FileService.GetFileAsync(int.Parse(folder), filename);
if (file != null)
{
success = true;
}
}
}
if (success)
{
upload++;
}
// if ChunkSize parameter is not overridden use the site setting
chunksize = int.Parse(SettingService.GetSetting(PageState.Site.Settings, "MaxChunkSize", "1"));
}
if (!ShowProgress)
{
_uploading = true;
StateHasChanged();
}
// upload files
var success = await interop.UploadFiles(posturl, folder, _guid, SiteState.AntiForgeryToken, jwt, chunksize, tokenSource.Token);
// reset progress indicators
if (ShowProgress)
{
@ -445,7 +421,7 @@
}
else
{
await logger.LogInformation("File Upload Failed Or Is Still In Progress {Files}", uploads);
await logger.LogError("File Upload Failed {Files}", uploads);
_message = Localizer["Error.File.Upload"];
_messagetype = MessageType.Error;
}
@ -475,6 +451,10 @@
_message = Localizer["Error.File.Upload"];
_messagetype = MessageType.Error;
_uploading = false;
await tokenSource.CancelAsync();
}
finally {
tokenSource.Dispose();
}
}

View File

@ -38,13 +38,10 @@
protected void OnChange(ChangeEventArgs e)
{
if (!string.IsNullOrEmpty(e.Value.ToString()))
Value = e.Value.ToString();
if (ValueChanged.HasDelegate)
{
Value = e.Value.ToString();
if (ValueChanged.HasDelegate)
{
ValueChanged.InvokeAsync(Value);
}
ValueChanged.InvokeAsync(Value);
}
}
}

View File

@ -10,13 +10,16 @@
{
<NavLink class="ms-2" href="@NavigateUrl("admin/log")">View Details</NavLink>
}
@if (ModuleState.RenderMode == RenderModes.Static)
@if (ModuleState != null)
{
<a href="@NavigationManager.Uri" class="btn-close" data-dismiss="alert" aria-label="close"></a>
}
else
{
<button type="button" class="btn-close" data-dismiss="alert" aria-label="close" @onclick="CloseMessage"></button>
@if (ModuleState.RenderMode == RenderModes.Static)
{
<a href="@NavigationManager.Uri" class="btn-close" data-dismiss="alert" aria-label="close"></a>
}
else
{
<button type="button" class="btn-close" data-dismiss="alert" aria-label="close" @onclick="CloseMessage"></button>
}
}
</div>
}

View File

@ -11,7 +11,7 @@
@if (!string.IsNullOrEmpty(SearchProperties))
{
<form autocomplete="off">
<div class="input-group my-3">
<div class="input-group my-3 @SearchBoxClass">
<input type="text" id="pagersearch" class="form-control" placeholder=@string.Format(Localizer["SearchPlaceholder"], FormatSearchProperties()) @bind="@_search" />
<button type="button" class="btn btn-primary" @onclick="Search">@SharedLocalizer["Search"]</button>
<button type="button" class="btn btn-secondary" @onclick="Reset">@SharedLocalizer["Reset"]</button>
@ -21,7 +21,7 @@
@if ((Toolbar == "Top" || Toolbar == "Both") && _pages > 0 && Items.Count() > _maxItems)
{
<ul class="pagination justify-content-center my-2">
<ul class="pagination justify-content-@PaginationAlignment.ToLower() my-2">
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
<a class="page-link shadow-none" @onclick=@(async () => UpdateList(1))><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
</li>
@ -74,7 +74,7 @@
{
<form method="post" autocomplete="off" @formname="PagerForm" @onsubmit="Search" data-enhance>
<input type="hidden" name="@Constants.RequestVerificationToken" value="@SiteState.AntiForgeryToken" />
<div class="input-group my-3">
<div class="input-group my-3 @SearchBoxClass">
<input type="text" id="pagersearch" name="_search" class="form-control" placeholder=@string.Format(Localizer["SearchPlaceholder"], FormatSearchProperties()) @bind="@_search" />
<button type="submit" class="btn btn-primary">@SharedLocalizer["Search"]</button>
<a class="btn btn-secondary" href="@PageUrl(1, "")">@SharedLocalizer["Reset"]</a>
@ -84,7 +84,7 @@
@if ((Toolbar == "Top" || Toolbar == "Both") && _pages > 0 && Items.Count() > _maxItems)
{
<ul class="pagination justify-content-center my-2">
<ul class="pagination justify-content-@PaginationAlignment.ToLower() my-2">
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
<a class="page-link shadow-none" href="@PageUrl(1, _search)"><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
</li>
@ -200,7 +200,7 @@
{
@if (PageState.RenderMode == RenderModes.Interactive || ModuleState.RenderMode == RenderModes.Interactive)
{
<ul class="pagination justify-content-center my-2">
<ul class="pagination justify-content-@PaginationAlignment.ToLower() my-2">
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
<a class="page-link shadow-none" @onclick=@(async () => UpdateList(1))><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
</li>
@ -248,7 +248,7 @@
}
else
{
<ul class="pagination justify-content-center my-2">
<ul class="pagination justify-content-@PaginationAlignment.ToLower() my-2">
<li class="page-item@((_page > 1) ? " app-pager-pointer" : " disabled")">
<a class="page-link shadow-none" href="@PageUrl(1, _search)"><span class="oi oi-media-step-backward" title="start" aria-hidden="true"></span></a>
</li>
@ -359,12 +359,21 @@
[Parameter]
public string SearchProperties { get; set; } // comma delimited list of property names to include in search
[Parameter]
public string SearchBoxClass { get; set; } // class for Search box
[Parameter]
public string Parameters { get; set; } // optional - querystring parameters in the form of "id=x&name=y" used in static render mode
[SupplyParameterFromForm(FormName = "PagerForm")]
public string _Search { get => ""; set => _search = value; }
/// <summary>
/// Accepted values are Start or Center or End. The default value is Center
/// </summary>
[Parameter]
public string PaginationAlignment { get; set; } = "center"; // Alignment of the Page Numbering start, center, end
private IEnumerable<TableItem> ItemList { get; set; }
protected override void OnInitialized()
@ -449,9 +458,9 @@
_displayPages = int.Parse(DisplayPages);
}
if (PageState.QueryString.ContainsKey("page"))
if (PageState.QueryString.ContainsKey("page") && int.TryParse(PageState.QueryString["page"], out int page))
{
_page = int.Parse(PageState.QueryString["page"]);
_page = page;
}
else
{

View File

@ -111,7 +111,7 @@
[Parameter]
public List<Permission> PermissionList { get; set; }
protected override async Task OnInitializedAsync()
protected override async Task OnInitializedAsync()
{
if (!string.IsNullOrEmpty(Permissions))
{

View File

@ -4,11 +4,11 @@ using System.Threading.Tasks;
namespace Oqtane.Modules.Controls
{
public class RichTextEditorInterop
public class QuillEditorInterop
{
private readonly IJSRuntime _jsRuntime;
public RichTextEditorInterop(IJSRuntime jsRuntime)
public QuillEditorInterop(IJSRuntime jsRuntime)
{
_jsRuntime = jsRuntime;
}

View File

@ -0,0 +1,578 @@
@namespace Oqtane.Modules.Controls
@inherits ModuleControlBase
@implements ITextEditor
@inject ISettingService SettingService
@inject NavigationManager NavigationManager
@inject IStringLocalizer<QuillJSTextEditor> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer
<div class="quill-text-editor">
<TabStrip ActiveTab="@_activetab">
@if (_allowRichText)
{
<TabPanel Name="Rich" Heading="Rich Text Editor" ResourceKey="RichTextEditor">
@if (_richfilemanager)
{
<FileManager @ref="_fileManager" Filter="@PageState.Site.ImageFiles" />
<ModuleMessage Message="@_message" Type="MessageType.Warning"></ModuleMessage>
<br />
}
<div class="d-flex justify-content-center mb-2">
@if (_allowFileManagement)
{
<button type="button" class="btn btn-primary" @onclick="InsertRichImage">@Localizer["InsertImage"]</button>
}
@if (_richfilemanager)
{
@((MarkupString)"&nbsp;&nbsp;")
<button type="button" class="btn btn-secondary" @onclick="CloseRichFileManager">@Localizer["Close"]</button>
}
</div>
<div class="row">
<div class="col">
<div @ref="@_toolBar">
@if (!string.IsNullOrEmpty(_toolbarContent))
{
@((MarkupString)_toolbarContent)
}
else
{
<select class="ql-header">
<option selected=""></option>
<option value="1"></option>
<option value="2"></option>
<option value="3"></option>
<option value="4"></option>
<option value="5"></option>
</select>
<span class="ql-formats">
<button class="ql-bold"></button>
<button class="ql-italic"></button>
<button class="ql-underline"></button>
<button class="ql-strike"></button>
</span>
<span class="ql-formats">
<select class="ql-color"></select>
<select class="ql-background"></select>
</span>
<span class="ql-formats">
<button class="ql-list" value="ordered"></button>
<button class="ql-list" value="bullet"></button>
</span>
<span class="ql-formats">
<button class="ql-link"></button>
</span>
}
</div>
<div @ref="@_editorElement"></div>
</div>
</div>
</TabPanel>
}
@if (_allowRawHtml)
{
<TabPanel Name="Raw" Heading="Raw HTML Editor" ResourceKey="HtmlEditor">
@if (_rawfilemanager)
{
<FileManager @ref="_fileManager" Filter="@PageState.Site.ImageFiles" />
<ModuleMessage Message="@_message" Type="MessageType.Warning"></ModuleMessage>
<br />
}
<div class="d-flex justify-content-center mb-2">
@if (_allowFileManagement)
{
<button type="button" class="btn btn-primary" @onclick="InsertRawImage">@Localizer["InsertImage"]</button>
}
@if (_rawfilemanager)
{
@((MarkupString)"&nbsp;&nbsp;")
<button type="button" class="btn btn-secondary" @onclick="CloseRawFileManager">@Localizer["Close"]</button>
}
</div>
@if (ReadOnly)
{
<textarea id="@_rawhtmlid" class="form-control" placeholder="@Placeholder" @bind="@_rawhtml" rows="10" readonly></textarea>
}
else
{
<textarea id="@_rawhtmlid" class="form-control" placeholder="@Placeholder" @bind="@_rawhtml" rows="10"></textarea>
}
</TabPanel>
}
@if (_allowSettings)
{
<TabPanel Name="Settings" Heading="Settings" ResourceKey="Settings">
<div class="quill-text-editor-settings">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="Scope" ResourceKey="Scope" ResourceType="@resourceType" HelpText="Specify if settings are scoped to the module or site">Scope: </Label>
<div class="col-sm-9">
<select id="Scope" class="form-select" value="@_scopeSetting" @onchange="(e => ScopeChanged(e))">
<option value="Module">@SharedLocalizer["Module"]</option>
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
{
<option value="Site">@SharedLocalizer["Site"]</option>
}
</select>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="AllowRichText" ResourceKey="AllowRichText" ResourceType="@resourceType" HelpText="Specify if editors can use the Rich Text Editor">Rich Text Editor? </Label>
<div class="col-sm-9">
<select id="AllowRichText" class="form-select" @bind="@_allowRichTextSetting" required>
<option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option>
</select>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="AllowRawHtml" ResourceKey="AllowRawHtml" ResourceType="@resourceType" HelpText="Specify if editors can use the Raw HTML Editor">Raw HTML Editor? </Label>
<div class="col-sm-9">
<select id="AllowRawHtml" class="form-select" @bind="@_allowRawHtmlSetting" required>
<option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option>
</select>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="AllowFileManagement" ResourceKey="AllowFileManagement" ResourceType="@resourceType" HelpText="Specify if editors can upload and insert images">Insert Images? </Label>
<div class="col-sm-9">
<select id="AllowFileManagement" class="form-select" @bind="@_allowFileManagementSetting" required>
<option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option>
</select>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="Theme" ResourceKey="Theme" ResourceType="@resourceType" HelpText="Specify the Rich Text Editor's theme">Theme: </Label>
<div class="col-sm-9">
<input type="text" id="Theme" class="form-control" @bind="_themeSetting" />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="DebugLevel" ResourceKey="DebugLevel" ResourceType="@resourceType" HelpText="Specify the Debug Level">Debug Level: </Label>
<div class="col-sm-9">
<select id="DebugLevel" class="form-select" @bind="_debugLevelSetting">
@foreach (var level in _debugLevels)
{
<option value="@level">@level</option>
}
</select>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="ToolbarContent" ResourceKey="ToolbarContent" ResourceType="@resourceType" HelpText="Specify any toolbar content to customize the Rich Text Editor">Toolbar Content: </Label>
<div class="col-sm-9">
<textarea id="ToolbarContent" class="form-control" @bind="_toolbarContentSetting" rows="3" />
</div>
</div>
<div class="row mb-1 align-items-center">
<div class="col-sm-9 offset-sm-3">
<button type="button" class="btn btn-success" @onclick="@(async () => await UpdateSettings())">@Localizer["SaveSettings"]</button>
</div>
</div>
</div>
</TabPanel>
}
</TabStrip>
</div>
@code {
public string Name => "QuillJS";
private string resourceType = "Oqtane.Modules.Controls.QuillJSTextEditor, Oqtane.Client";
private bool _settingsLoaded;
private bool _initialized = false;
private QuillEditorInterop _interop;
private FileManager _fileManager;
private string _activetab = "Rich";
private bool _allowSettings = false;
private bool _allowFileManagement = false;
private bool _allowRawHtml = false;
private bool _allowRichText = false;
private string _theme = "snow";
private string _debugLevel = "info";
private string _toolbarContent = string.Empty;
private string _scopeSetting = "Module";
private string _allowFileManagementSetting = "False";
private string _allowRawHtmlSetting = "False";
private string _allowRichTextSetting = "False";
private string _themeSetting = "snow";
private string _debugLevelSetting = "info";
private string _toolbarContentSetting = string.Empty;
private ElementReference _editorElement;
private ElementReference _toolBar;
private bool _richfilemanager = false;
private string _richhtml = string.Empty;
private string _originalrichhtml = string.Empty;
private bool _rawfilemanager = false;
private string _rawhtmlid = "RawHtmlEditor_" + Guid.NewGuid().ToString("N");
private string _rawhtml = string.Empty;
private string _originalrawhtml = string.Empty;
private string _message = string.Empty;
private bool _contentchanged = false;
private int _editorIndex;
private List<string> _debugLevels = new List<string> { "info", "log", "warn", "error" };
[Parameter]
public bool ReadOnly { get; set; }
[Parameter]
public string Placeholder { get; set; }
// the following parameters were supported by the original RichTextEditor and can be passed as optional static parameters
[Parameter]
public bool? AllowFileManagement { get; set; }
[Parameter]
public bool? AllowRichText { get; set; }
[Parameter]
public bool? AllowRawHtml { get; set; }
[Parameter]
public string Theme { get; set; }
[Parameter]
public string DebugLevel { get; set; }
public override List<Resource> Resources { get; set; } = new List<Resource>()
{
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill.min.js", Location = ResourceLocation.Body },
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill-blot-formatter.min.js", Location = ResourceLocation.Body },
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill-interop.js", Location = ResourceLocation.Body }
};
protected override void OnInitialized()
{
_interop = new QuillEditorInterop(JSRuntime);
if (string.IsNullOrEmpty(Placeholder))
{
Placeholder = Localizer["Placeholder"];
}
}
protected override void OnParametersSet()
{
LoadSettings();
if (!_allowRichText)
{
_activetab = "Raw";
}
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
// include CSS theme
var interop = new Interop(JSRuntime);
await interop.IncludeLink("", "stylesheet", $"{PageState?.Alias.BaseUrl}/css/quill/quill.{_theme}.css", "text/css", "", "", "");
}
await base.OnAfterRenderAsync(firstRender);
if (_allowRichText)
{
if (firstRender)
{
await _interop.CreateEditor(
_editorElement,
_toolBar,
ReadOnly,
Placeholder,
_theme,
_debugLevel);
await _interop.LoadEditorContent(_editorElement, _richhtml);
// preserve a copy of the content (Quill sanitizes content so we need to retrieve it from the editor as it may have been modified)
_originalrichhtml = await _interop.GetHtml(_editorElement);
_initialized = true;
}
else
{
if (_initialized)
{
if (_contentchanged)
{
// reload editor if Content passed to component has changed
await _interop.LoadEditorContent(_editorElement, _richhtml);
_originalrichhtml = await _interop.GetHtml(_editorElement);
_contentchanged = false;
}
else
{
// preserve changed content on re-render event
var richhtml = await _interop.GetHtml(_editorElement);
if (richhtml != _richhtml)
{
_richhtml = richhtml;
await _interop.LoadEditorContent(_editorElement, _richhtml);
}
}
}
}
}
}
public void Initialize(string content)
{
_richhtml = content;
_rawhtml = content;
_originalrichhtml = "";
_richhtml = content;
if (!_contentchanged)
{
_contentchanged = content != _originalrawhtml;
}
_originalrawhtml = _rawhtml; // preserve for comparison later
StateHasChanged();
}
public async Task<string> GetContent()
{
// evaluate raw html content as first priority
if (_rawhtml != _originalrawhtml)
{
return _rawhtml;
}
else
{
var richhtml = "";
if (_allowRichText)
{
richhtml = await _interop.GetHtml(_editorElement);
}
if (richhtml != _originalrichhtml && !string.IsNullOrEmpty(richhtml))
{
// convert Quill's empty content to empty string
if (richhtml == "<p><br></p>")
{
richhtml = string.Empty;
}
return richhtml;
}
else
{
// return original raw html content
return _originalrawhtml;
}
}
}
public void CloseRichFileManager()
{
_richfilemanager = false;
_message = string.Empty;
StateHasChanged();
}
public void CloseRawFileManager()
{
_rawfilemanager = false;
_message = string.Empty;
StateHasChanged();
}
public async Task<string> GetHtml()
{
// evaluate raw html content as first priority
if (_rawhtml != _originalrawhtml)
{
return _rawhtml;
}
else
{
var richhtml = "";
if (_allowRichText)
{
richhtml = await _interop.GetHtml(_editorElement);
}
if (richhtml != _originalrichhtml && !string.IsNullOrEmpty(richhtml))
{
// convert Quill's empty content to empty string
if (richhtml == "<p><br></p>")
{
richhtml = string.Empty;
}
return richhtml;
}
else
{
// return original raw html content
return _originalrawhtml;
}
}
}
public async Task InsertRichImage()
{
_message = string.Empty;
if (_richfilemanager)
{
var file = _fileManager.GetFile();
if (file != null)
{
await _interop.InsertImage(_editorElement, file.Url, ((!string.IsNullOrEmpty(file.Description)) ? file.Description : file.Name), _editorIndex);
_richhtml = await _interop.GetHtml(_editorElement);
_richfilemanager = false;
}
else
{
_message = Localizer["Message.Require.Image"];
}
}
else
{
_editorIndex = await _interop.GetCurrentCursor(_editorElement);
_richfilemanager = true;
}
StateHasChanged();
}
public async Task InsertRawImage()
{
_message = string.Empty;
if (_rawfilemanager)
{
var file = _fileManager.GetFile();
if (file != null)
{
var interop = new Interop(JSRuntime);
int pos = await interop.GetCaretPosition(_rawhtmlid);
var image = "<img src=\"" + file.Url + "\" alt=\"" + ((!string.IsNullOrEmpty(file.Description)) ? file.Description : file.Name) + "\" class=\"img-fluid\">";
_rawhtml = _rawhtml.Substring(0, pos) + image + _rawhtml.Substring(pos);
_rawfilemanager = false;
}
else
{
_message = Localizer["Message.Require.Image"];
}
}
else
{
_rawfilemanager = true;
}
StateHasChanged();
}
private void ScopeChanged(ChangeEventArgs e)
{
_scopeSetting = (string)e.Value;
LoadSettings();
}
private void LoadSettings(bool reload = false)
{
try
{
if (!_settingsLoaded || reload)
{
_allowFileManagement = bool.Parse(GetSetting("Component", "QuillTextEditor_AllowFileManagement", "True"));
_allowRawHtml = bool.Parse(GetSetting("Component", "QuillTextEditor_AllowRawHtml", "True"));
_allowRichText = bool.Parse(GetSetting("Component", "QuillTextEditor_AllowRichText", "True"));
_theme = GetSetting("Component", "QuillTextEditor_Theme", "snow");
_debugLevel = GetSetting("Component", "QuillTextEditor_DebugLevel", "info");
_toolbarContent = GetSetting("Component", "QuillTextEditor_ToolbarContent", string.Empty);
// optional static parameter overrides
if (AllowFileManagement != null) _allowFileManagement = AllowFileManagement.Value;
if (AllowRichText != null) _allowRichText = AllowRichText.Value;
if (AllowRawHtml != null) _allowRawHtml = AllowRawHtml.Value;
if (!string.IsNullOrEmpty(Theme)) _theme = Theme;
if (!string.IsNullOrEmpty(DebugLevel)) _debugLevel = DebugLevel;
}
_allowSettings = PageState.EditMode && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.PermissionList);
if (_allowSettings)
{
_allowFileManagementSetting = GetSetting(_scopeSetting, "QuillTextEditor_AllowFileManagement", "True");
_allowRawHtmlSetting = GetSetting(_scopeSetting, "QuillTextEditor_AllowRawHtml", "True");
_allowRichTextSetting = GetSetting(_scopeSetting, "QuillTextEditor_AllowRichText", "True");
_themeSetting = GetSetting(_scopeSetting, "QuillTextEditor_Theme", "snow");
_debugLevelSetting = GetSetting(_scopeSetting, "QuillTextEditor_DebugLevel", "info");
_toolbarContentSetting = GetSetting(_scopeSetting, "QuillTextEditor_ToolbarContent", string.Empty);
}
_settingsLoaded = true;
}
catch (Exception ex)
{
AddModuleMessage(ex.Message, MessageType.Error);
}
}
private string GetSetting(string scope, string settingName, string defaultValue)
{
var settingValue = "";
switch (scope)
{
case "Component":
settingValue = SettingService.GetSetting(PageState.Site.Settings, settingName, defaultValue);
settingValue = SettingService.GetSetting(ModuleState.Settings, settingName, settingValue);
break;
case "Site":
settingValue = SettingService.GetSetting(PageState.Site.Settings, settingName, defaultValue);
break;
case "Module":
settingValue = SettingService.GetSetting(ModuleState.Settings, settingName, defaultValue);
break;
}
return settingValue;
}
private async Task UpdateSettings()
{
try
{
if (_scopeSetting == "Site" && UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
{
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId);
settings = SettingService.SetSetting(settings, "QuillTextEditor_AllowFileManagement", _allowFileManagementSetting);
settings = SettingService.SetSetting(settings, "QuillTextEditor_AllowRawHtml", _allowRawHtmlSetting);
settings = SettingService.SetSetting(settings, "QuillTextEditor_AllowRichText", _allowRichTextSetting);
settings = SettingService.SetSetting(settings, "QuillTextEditor_Theme", _themeSetting);
settings = SettingService.SetSetting(settings, "QuillTextEditor_DebugLevel", _debugLevelSetting);
settings = SettingService.SetSetting(settings, "QuillTextEditor_ToolbarContent", _toolbarContentSetting);
await SettingService.UpdateSiteSettingsAsync(settings, PageState.Site.SiteId);
}
else if (_scopeSetting == "Module")
{
var settings = await SettingService.GetModuleSettingsAsync(ModuleState.ModuleId);
settings = SettingService.SetSetting(settings, "QuillTextEditor_AllowFileManagement", _allowFileManagementSetting);
settings = SettingService.SetSetting(settings, "QuillTextEditor_AllowRawHtml", _allowRawHtmlSetting);
settings = SettingService.SetSetting(settings, "QuillTextEditor_AllowRichText", _allowRichTextSetting);
settings = SettingService.SetSetting(settings, "QuillTextEditor_Theme", _themeSetting);
settings = SettingService.SetSetting(settings, "QuillTextEditor_DebugLevel", _debugLevelSetting);
settings = SettingService.SetSetting(settings, "QuillTextEditor_ToolbarContent", _toolbarContentSetting);
await SettingService.UpdateModuleSettingsAsync(settings,ModuleState.ModuleId);
}
LoadSettings(true);
NavigationManager.NavigateTo(NavigationManager.Uri, true);
}
catch (Exception ex)
{
AddModuleMessage(ex.Message, MessageType.Error);
}
}
}

View File

@ -1,128 +1,22 @@
@using System.Text.RegularExpressions
@using Microsoft.AspNetCore.Components.Rendering
@using Microsoft.Extensions.DependencyInjection
@namespace Oqtane.Modules.Controls
@inherits ModuleControlBase
@inject IServiceProvider ServiceProvider
@inject ISettingService SettingService
@inject IStringLocalizer<RichTextEditor> Localizer
<div class="row" style="margin-bottom: 50px;">
<div class="col">
<TabStrip ActiveTab="@_activetab">
@if (AllowRichText)
{
<TabPanel Name="Rich" Heading="Rich Text Editor" ResourceKey="RichTextEditor">
@if (_richfilemanager)
{
<FileManager @ref="_fileManager" Filter="@PageState.Site.ImageFiles" />
<ModuleMessage Message="@_message" Type="MessageType.Warning"></ModuleMessage>
<br />
}
<div class="d-flex justify-content-center mb-2">
@if (AllowFileManagement)
{
<button type="button" class="btn btn-primary" @onclick="InsertRichImage">@Localizer["InsertImage"]</button>
}
@if (_richfilemanager)
{
@((MarkupString)"&nbsp;&nbsp;")
<button type="button" class="btn btn-secondary" @onclick="CloseRichFileManager">@Localizer["Close"]</button>
}
</div>
<div class="row">
<div class="col">
<div @ref="@_toolBar">
@if (ToolbarContent != null)
{
@ToolbarContent
}
else
{
<select class="ql-header">
<option selected=""></option>
<option value="1"></option>
<option value="2"></option>
<option value="3"></option>
<option value="4"></option>
<option value="5"></option>
</select>
<span class="ql-formats">
<button class="ql-bold"></button>
<button class="ql-italic"></button>
<button class="ql-underline"></button>
<button class="ql-strike"></button>
</span>
<span class="ql-formats">
<select class="ql-color"></select>
<select class="ql-background"></select>
</span>
<span class="ql-formats">
<button class="ql-list" value="ordered"></button>
<button class="ql-list" value="bullet"></button>
</span>
<span class="ql-formats">
<button class="ql-link"></button>
</span>
}
</div>
<div @ref="@_editorElement"></div>
</div>
</div>
</TabPanel>
}
@if (AllowRawHtml)
{
<TabPanel Name="Raw" Heading="Raw HTML Editor" ResourceKey="HtmlEditor">
@if (_rawfilemanager)
{
<FileManager @ref="_fileManager" Filter="@PageState.Site.ImageFiles" />
<ModuleMessage Message="@_message" Type="MessageType.Warning"></ModuleMessage>
<br />
}
<div class="d-flex justify-content-center mb-2">
@if (AllowFileManagement)
{
<button type="button" class="btn btn-primary" @onclick="InsertRawImage">@Localizer["InsertImage"]</button>
}
@if (_rawfilemanager)
{
@((MarkupString)"&nbsp;&nbsp;")
<button type="button" class="btn btn-secondary" @onclick="CloseRawFileManager">@Localizer["Close"]</button>
}
</div>
@if (ReadOnly)
{
<textarea id="@_rawhtmlid" class="form-control" placeholder="@Placeholder" @bind="@_rawhtml" rows="10" readonly></textarea>
}
else
{
<textarea id="@_rawhtmlid" class="form-control" placeholder="@Placeholder" @bind="@_rawhtml" rows="10"></textarea>
}
</TabPanel>
}
</TabStrip>
@_textEditorComponent
</div>
</div>
@code {
private bool _initialized = false;
private RichTextEditorInterop interop;
private FileManager _fileManager;
private string _activetab = "Rich";
private ElementReference _editorElement;
private ElementReference _toolBar;
private bool _richfilemanager = false;
private string _richhtml = string.Empty;
private string _originalrichhtml = string.Empty;
private bool _rawfilemanager = false;
private string _rawhtmlid = "RawHtmlEditor_" + Guid.NewGuid().ToString("N");
private string _rawhtml = string.Empty;
private string _originalrawhtml = string.Empty;
private string _message = string.Empty;
private bool _contentchanged = false;
private int _editorIndex;
private string _textEditorType;
private RenderFragment _textEditorComponent;
private ITextEditor _textEditor;
[Parameter]
public string Content { get; set; }
@ -134,203 +28,100 @@
public string Placeholder { get; set; }
[Parameter]
public bool AllowFileManagement { get; set; } = true;
public string Provider { get; set; }
[Parameter]
public bool AllowRichText { get; set; } = true;
[Parameter]
public bool AllowRawHtml { get; set; } = true;
// parameters only applicable to rich text editor
[Parameter]
public RenderFragment ToolbarContent { get; set; }
[Parameter]
public string Theme { get; set; } = "snow";
[Parameter]
public string DebugLevel { get; set; } = "info";
public override List<Resource> Resources => new List<Resource>()
{
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill.min.js", Location = ResourceLocation.Body },
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill-blot-formatter.min.js", Location = ResourceLocation.Body },
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill-interop.js", Location = ResourceLocation.Body }
};
[Parameter(CaptureUnmatchedValues = true)]
public Dictionary<string, object> AdditionalAttributes { get; set; } = new Dictionary<string, object>();
protected override void OnInitialized()
{
interop = new RichTextEditorInterop(JSRuntime);
if (string.IsNullOrEmpty(Placeholder))
{
Placeholder = Localizer["Placeholder"];
}
_textEditorType = GetTextEditorType();
}
protected override void OnParametersSet()
{
_richhtml = Content;
_rawhtml = Content;
_originalrawhtml = _rawhtml; // preserve for comparison later
_originalrichhtml = "";
if (Content != _originalrawhtml)
_textEditorComponent = (builder) =>
{
_contentchanged = true; // identifies when Content parameter has changed
}
if (!AllowRichText)
{
_activetab = "Raw";
}
CreateTextEditor(builder);
};
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await base.OnAfterRenderAsync(firstRender);
if (AllowRichText)
if(_textEditor != null)
{
if (firstRender)
{
await interop.CreateEditor(
_editorElement,
_toolBar,
ReadOnly,
Placeholder,
Theme,
DebugLevel);
await interop.LoadEditorContent(_editorElement, _richhtml);
// preserve a copy of the content (Quill sanitizes content so we need to retrieve it from the editor as it may have been modified)
_originalrichhtml = await interop.GetHtml(_editorElement);
_initialized = true;
}
else
{
if (_initialized)
{
if (_contentchanged)
{
// reload editor if Content passed to component has changed
await interop.LoadEditorContent(_editorElement, _richhtml);
_originalrichhtml = await interop.GetHtml(_editorElement);
}
else
{
// preserve changed content on re-render event
var richhtml = await interop.GetHtml(_editorElement);
if (richhtml != _richhtml)
{
_richhtml = richhtml;
await interop.LoadEditorContent(_editorElement, _richhtml);
}
}
}
}
_contentchanged = false;
_textEditor.Initialize(Content);
}
}
public void CloseRichFileManager()
{
_richfilemanager = false;
_message = string.Empty;
StateHasChanged();
}
public void CloseRawFileManager()
{
_rawfilemanager = false;
_message = string.Empty;
StateHasChanged();
await base.OnAfterRenderAsync(firstRender);
}
public async Task<string> GetHtml()
{
// evaluate raw html content as first priority
if (_rawhtml != _originalrawhtml)
{
return _rawhtml;
}
else
{
var richhtml = "";
return await _textEditor.GetContent();
}
if (AllowRichText)
private void CreateTextEditor(RenderTreeBuilder builder)
{
if(!string.IsNullOrEmpty(_textEditorType))
{
var editorType = Type.GetType(_textEditorType);
if (editorType != null)
{
richhtml = await interop.GetHtml(_editorElement);
}
builder.OpenComponent(0, editorType);
if (richhtml != _originalrichhtml && !string.IsNullOrEmpty(richhtml))
{
// convert Quill's empty content to empty string
if (richhtml == "<p><br></p>")
var attributes = new Dictionary<string, object>
{
richhtml = string.Empty;
{ "Placeholder", Placeholder },
{ "ReadOnly", ReadOnly }
};
if (AdditionalAttributes != null)
{
foreach(var key in AdditionalAttributes.Keys)
{
if(!attributes.ContainsKey(key))
{
attributes.Add(key, AdditionalAttributes[key]);
}
else
{
attributes[key] = AdditionalAttributes[key];
}
}
}
return richhtml;
}
else
{
// return original raw html content
return _originalrawhtml;
var index = 1;
foreach(var name in attributes.Keys)
{
if (editorType.GetProperty(name) != null)
{
builder.AddAttribute(index++, name, attributes[name]);
}
}
builder.AddComponentReferenceCapture(index, (c) =>
{
_textEditor = (ITextEditor)c;
});
builder.CloseComponent();
}
}
}
public async Task InsertRichImage()
private string GetTextEditorType()
{
_message = string.Empty;
if (_richfilemanager)
{
var file = _fileManager.GetFile();
if (file != null)
{
await interop.InsertImage(_editorElement, file.Url, ((!string.IsNullOrEmpty(file.Description)) ? file.Description : file.Name), _editorIndex);
_richhtml = await interop.GetHtml(_editorElement);
_richfilemanager = false;
}
else
{
_message = Localizer["Message.Require.Image"];
}
}
else
{
_editorIndex = await interop.GetCurrentCursor(_editorElement);
_richfilemanager = true;
}
StateHasChanged();
}
const string EditorSettingName = "TextEditor";
public async Task InsertRawImage()
{
_message = string.Empty;
if (_rawfilemanager)
{
var file = _fileManager.GetFile();
if (file != null)
{
var interop = new Interop(JSRuntime);
int pos = await interop.GetCaretPosition(_rawhtmlid);
var image = "<img src=\"" + file.Url + "\" alt=\"" + ((!string.IsNullOrEmpty(file.Description)) ? file.Description : file.Name) + "\" class=\"img-fluid\">";
_rawhtml = _rawhtml.Substring(0, pos) + image + _rawhtml.Substring(pos);
_rawfilemanager = false;
}
else
{
_message = Localizer["Message.Require.Image"];
}
}
else
{
_rawfilemanager = true;
}
StateHasChanged();
}
if(!string.IsNullOrEmpty(Provider))
{
var provider = ServiceProvider.GetServices<ITextEditor>().FirstOrDefault(i => i.Name.Equals(Provider, StringComparison.OrdinalIgnoreCase));
if(provider != null)
{
return Utilities.GetFullTypeName(provider.GetType().AssemblyQualifiedName);
}
}
return SettingService.GetSetting(PageState.Site.Settings, EditorSettingName, Constants.DefaultTextEditor);
}
}

View File

@ -36,14 +36,7 @@ else
Parent.AddTabPanel((TabPanel)this);
if (string.IsNullOrEmpty(Heading))
{
Heading = Localize(nameof(Name), Name);
}
else
{
Heading = Localize(nameof(Heading), Heading);
}
Heading = string.IsNullOrEmpty(Heading) ? Localize(nameof(Name), Name) : Localize(nameof(Heading), Heading);
}
public string DisplayHeading()

View File

@ -23,7 +23,7 @@
</li>
}
</ul>
<div class="tab-content">
<div class="tab-content @TabContentClass">
<br />
@ChildContent
</div>
@ -47,6 +47,9 @@
[Parameter]
public string Id { get; set; } // optional - used to uniquely identify an instance of a tab strip component (will be set automatically if no value provided)
[Parameter]
public string TabContentClass { get; set; } // optional - to extend the TabContent div.
protected override void OnInitialized()
{
if (string.IsNullOrEmpty(Id))

View File

@ -0,0 +1,33 @@
@namespace Oqtane.Modules.Controls
@inherits ModuleControlBase
@implements ITextEditor
<div class="text-area-editor">
<textarea @bind="_content" @ref="_editor" placeholder="@Placeholder" readonly="@ReadOnly" />
</div>
@code {
public string Name => "TextArea";
private ElementReference _editor;
private string _content;
[Parameter]
public bool ReadOnly { get; set; }
[Parameter]
public string Placeholder { get; set; }
public void Initialize(string content)
{
_content = content;
StateHasChanged();
}
public async Task<string> GetContent()
{
await Task.CompletedTask;
return _content;
}
}

View File

@ -13,7 +13,7 @@
<TabPanel Name="Edit" Heading="Edit" ResourceKey="Edit">
@if (_content != null)
{
<RichTextEditor Content="@_content" AllowFileManagement="@_allowfilemanagement" AllowRawHtml="@_allowrawhtml" @ref="@RichTextEditorHtml"></RichTextEditor>
<RichTextEditor Content="@_content" @ref="@RichTextEditorHtml"></RichTextEditor>
<br />
<button type="button" class="btn btn-success" @onclick="SaveContent">@SharedLocalizer["Save"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
@ -47,44 +47,34 @@
</TabStrip>
@code {
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
public override string Title => "Edit Html/Text";
public override string Title => "Edit Html/Text";
public override List<Resource> Resources => new List<Resource>()
{
new Resource { ResourceType = ResourceType.Stylesheet, Url = "css/quill/quill.bubble.css" },
new Resource { ResourceType = ResourceType.Stylesheet, Url = "css/quill/quill.snow.css" }
};
private RichTextEditor RichTextEditorHtml;
private string _content = null;
private string _createdby;
private DateTime _createdon;
private string _modifiedby;
private DateTime _modifiedon;
private List<Models.HtmlText> _htmltexts;
private string _view = "";
private RichTextEditor RichTextEditorHtml;
private bool _allowfilemanagement;
private bool _allowrawhtml;
private string _content = null;
private string _createdby;
private DateTime _createdon;
private string _modifiedby;
private DateTime _modifiedon;
private List<Models.HtmlText> _htmltexts;
private string _view = "";
protected override async Task OnInitializedAsync()
{
try
{
await LoadContent();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading Content {Error}", ex.Message);
AddModuleMessage(Localizer["Error.Content.Load"], MessageType.Error);
}
}
protected override async Task OnInitializedAsync()
{
try
{
_allowfilemanagement = bool.Parse(SettingService.GetSetting(ModuleState.Settings, "AllowFileManagement", "true"));
_allowrawhtml = bool.Parse(SettingService.GetSetting(ModuleState.Settings, "AllowRawHtml", "true"));
await LoadContent();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading Content {Error}", ex.Message);
AddModuleMessage(Localizer["Error.Content.Load"], MessageType.Error);
}
}
private async Task LoadContent()
{
private async Task LoadContent()
{
var htmltext = await HtmlTextService.GetHtmlTextAsync(ModuleState.ModuleId);
if (htmltext != null)
{

View File

@ -2,6 +2,7 @@
@namespace Oqtane.Modules.HtmlText
@inherits ModuleBase
@inject IHtmlTextService HtmlTextService
@inject ISettingService SettingService
@inject IStringLocalizer<Index> Localizer
@if (PageState.EditMode)
@ -36,6 +37,10 @@
{
content = htmltext.Content;
content = Utilities.FormatContent(content, PageState.Alias, "render");
if (bool.Parse(SettingService.GetSetting(ModuleState.Settings, "DynamicTokens", "false")))
{
content = ReplaceTokens(content);
}
}
else
{

View File

@ -5,39 +5,34 @@
@inject IStringLocalizer<Settings> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer
<div class="container">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="files" ResourceKey="AllowFileManagement" ResourceType="@resourceType" HelpText="Specify If Editors Can Upload and Select Files">Allow File Management: </Label>
<div class="col-sm-9">
<select id="files" class="form-select" @bind="@_allowfilemanagement">
<option value="true">@SharedLocalizer["Yes"]</option>
<option value="false">@SharedLocalizer["No"]</option>
</select>
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
<div class="container">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="dynamictokens" ResourceKey="DynamicTokens" ResourceType="@resourceType" HelpText="Do you wish to allow tokens to be dynamically replaced? Please note that this will affect the performance of your site.">Dynamic Tokens? </Label>
<div class="col-sm-9">
<select id="dynamictokens" class="form-select" @bind="@_dynamictokens">
<option value="true">@SharedLocalizer["Yes"]</option>
<option value="false">@SharedLocalizer["No"]</option>
</select>
</div>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="files" ResourceKey="AllowRawHtml" ResourceType="@resourceType" HelpText="Specify If Editors Can Enter Raw HTML">Allow Raw HTML: </Label>
<div class="col-sm-9">
<select id="files" class="form-select" @bind="@_allowrawhtml">
<option value="true">@SharedLocalizer["Yes"]</option>
<option value="false">@SharedLocalizer["No"]</option>
</select>
</div>
</div>
</div>
</form>
@code {
private string resourceType = "Oqtane.Modules.HtmlText.Settings, Oqtane.Client"; // for localization
private string _allowfilemanagement;
private string _allowrawhtml;
@code {
private string resourceType = "Oqtane.Modules.HtmlText.Settings, Oqtane.Client"; // for localization
private ElementReference form;
private bool validated = false;
private string _dynamictokens;
protected override void OnInitialized()
{
try
{
_allowfilemanagement = SettingService.GetSetting(ModuleState.Settings, "AllowFileManagement", "true");
_allowrawhtml = SettingService.GetSetting(ModuleState.Settings, "AllowRawHtml", "true");
}
_dynamictokens = SettingService.GetSetting(ModuleState.Settings, "DynamicTokens", "false");
}
catch (Exception ex)
{
AddModuleMessage(ex.Message, MessageType.Error);
@ -48,10 +43,9 @@
{
try
{
var settings = await SettingService.GetModuleSettingsAsync(ModuleState.ModuleId);
settings = SettingService.SetSetting(settings, "AllowFileManagement", _allowfilemanagement);
settings = SettingService.SetSetting(settings, "AllowRawHtml", _allowrawhtml);
await SettingService.UpdateModuleSettingsAsync(settings, ModuleState.ModuleId);
var settings = await SettingService.GetModuleSettingsAsync(ModuleState.ModuleId);
settings = SettingService.SetSetting(settings, "DynamicTokens", _dynamictokens);
await SettingService.UpdateModuleSettingsAsync(settings, ModuleState.ModuleId);
}
catch (Exception ex)
{

View File

@ -10,6 +10,7 @@ using System.Collections.Generic;
using Microsoft.JSInterop;
using System.Linq;
using System.Dynamic;
using System.Reflection;
namespace Oqtane.Modules
{
@ -18,6 +19,7 @@ namespace Oqtane.Modules
private Logger _logger;
private string _urlparametersstate;
private Dictionary<string, string> _urlparameters;
private bool _scriptsloaded = false;
protected Logger logger => _logger ?? (_logger = new Logger(this));
@ -34,7 +36,7 @@ namespace Oqtane.Modules
protected PageState PageState { get; set; }
[CascadingParameter]
protected Module ModuleState { get; set; }
protected Models.Module ModuleState { get; set; }
[Parameter]
public RenderModeBoundary RenderModeBoundary { get; set; }
@ -103,12 +105,12 @@ namespace Oqtane.Modules
if (!string.IsNullOrEmpty(resource.Url))
{
var url = (resource.Url.Contains("://")) ? resource.Url : PageState.Alias.BaseUrl + resource.Url;
scripts.Add(new { href = url, bundle = resource.Bundle ?? "", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", es6module = resource.ES6Module, location = resource.Location.ToString().ToLower() });
scripts.Add(new { href = url, type = resource.Type ?? "", bundle = resource.Bundle ?? "", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", location = resource.Location.ToString().ToLower(), dataAttributes = resource.DataAttributes });
}
else
{
inline += 1;
await interop.IncludeScript(GetType().Namespace.ToLower() + inline.ToString(), "", "", "", resource.Content, resource.Location.ToString().ToLower());
await interop.IncludeScript(GetType().Namespace.ToLower() + inline.ToString(), "", "", "", resource.Type ?? "", resource.Content, resource.Location.ToString().ToLower());
}
}
}
@ -117,6 +119,7 @@ namespace Oqtane.Modules
await interop.IncludeScripts(scripts.ToArray());
}
}
_scriptsloaded = true;
}
}
@ -125,6 +128,14 @@ namespace Oqtane.Modules
return PageState?.RenderId == ModuleState?.RenderId;
}
public bool ScriptsLoaded
{
get
{
return _scriptsloaded;
}
}
// path method
public string ModulePath()
@ -132,8 +143,18 @@ namespace Oqtane.Modules
return PageState?.Alias.BaseUrl + "/Modules/" + GetType().Namespace + "/";
}
// fingerprint hash code for static assets
public string Fingerprint
{
get
{
return ModuleState.ModuleDefinition.Fingerprint;
}
}
// url methods
// navigate url
public string NavigateUrl()
{
return NavigateUrl(PageState.Page.Path);
@ -149,24 +170,65 @@ namespace Oqtane.Modules
return NavigateUrl(PageState.Page.Path, refresh);
}
public string NavigateUrl(string path, string parameters)
public string NavigateUrl(string path, string querystring)
{
return Utilities.NavigateUrl(PageState.Alias.Path, path, parameters);
return Utilities.NavigateUrl(PageState.Alias.Path, path, querystring);
}
public string NavigateUrl(string path, Dictionary<string, string> querystring)
{
return NavigateUrl(path, Utilities.CreateQueryString(querystring));
}
public string NavigateUrl(string path, bool refresh)
{
return Utilities.NavigateUrl(PageState.Alias.Path, path, refresh ? "refresh" : "");
return NavigateUrl(path, refresh ? "refresh" : "");
}
public string NavigateUrl(int moduleId, string action)
{
return EditUrl(PageState.Page.Path, moduleId, action, "");
}
public string NavigateUrl(int moduleId, string action, string querystring)
{
return EditUrl(PageState.Page.Path, moduleId, action, querystring);
}
public string NavigateUrl(int moduleId, string action, Dictionary<string, string> querystring)
{
return EditUrl(PageState.Page.Path, moduleId, action, querystring);
}
public string NavigateUrl(string path, int moduleId, string action)
{
return EditUrl(path, moduleId, action, "");
}
public string NavigateUrl(string path, int moduleId, string action, string querystring)
{
return EditUrl(path, moduleId, action, querystring);
}
public string NavigateUrl(string path, int moduleId, string action, Dictionary<string, string> querystring)
{
return EditUrl(path, moduleId, action, querystring);
}
// edit url
public string EditUrl(string action)
{
return EditUrl(ModuleState.ModuleId, action);
}
public string EditUrl(string action, string parameters)
public string EditUrl(string action, string querystring)
{
return EditUrl(ModuleState.ModuleId, action, parameters);
return EditUrl(ModuleState.ModuleId, action, querystring);
}
public string EditUrl(string action, Dictionary<string, string> querystring)
{
return EditUrl(ModuleState.ModuleId, action, querystring);
}
public string EditUrl(int moduleId, string action)
@ -174,16 +236,27 @@ namespace Oqtane.Modules
return EditUrl(moduleId, action, "");
}
public string EditUrl(int moduleId, string action, string parameters)
public string EditUrl(int moduleId, string action, string querystring)
{
return EditUrl(PageState.Page.Path, moduleId, action, parameters);
return EditUrl(PageState.Page.Path, moduleId, action, querystring);
}
public string EditUrl(string path, int moduleid, string action, string parameters)
public string EditUrl(int moduleId, string action, Dictionary<string, string> querystring)
{
return Utilities.EditUrl(PageState.Alias.Path, path, moduleid, action, parameters);
return EditUrl(PageState.Page.Path, moduleId, action, querystring);
}
public string EditUrl(string path, int moduleid, string action, string querystring)
{
return Utilities.EditUrl(PageState.Alias.Path, path, moduleid, action, querystring);
}
public string EditUrl(string path, int moduleid, string action, Dictionary<string, string> querystring)
{
return EditUrl(path, moduleid, action, Utilities.CreateQueryString(querystring));
}
// file url
public string FileUrl(string folderpath, string filename)
{
return FileUrl(folderpath, filename, false);
@ -203,6 +276,8 @@ namespace Oqtane.Modules
return Utilities.FileUrl(PageState.Alias, fileid, download);
}
// image url
public string ImageUrl(int fileid, int width, int height)
{
return ImageUrl(fileid, width, height, "");
@ -339,6 +414,79 @@ namespace Oqtane.Modules
await interop.ScrollTo(0, 0, "smooth");
}
public string ReplaceTokens(string content)
{
return ReplaceTokens(content, null);
}
public string ReplaceTokens(string content, object obj)
{
var tokens = new List<string>();
var pos = content.IndexOf("[");
if (pos != -1)
{
if (content.IndexOf("]", pos) != -1)
{
var token = content.Substring(pos, content.IndexOf("]", pos) - pos + 1);
if (token.Contains(":"))
{
tokens.Add(token.Substring(1, token.Length - 2));
}
}
pos = content.IndexOf("[", pos + 1);
}
if (tokens.Count != 0)
{
foreach (string token in tokens)
{
var segments = token.Split(":");
if (segments.Length >= 2 && segments.Length <= 3)
{
var objectName = string.Join(":", segments, 0, segments.Length - 1);
var propertyName = segments[segments.Length - 1];
var propertyValue = "";
switch (objectName)
{
case "ModuleState":
propertyValue = ModuleState.GetType().GetProperty(propertyName)?.GetValue(ModuleState, null).ToString();
break;
case "PageState":
propertyValue = PageState.GetType().GetProperty(propertyName)?.GetValue(PageState, null).ToString();
break;
case "PageState:Alias":
propertyValue = PageState.Alias.GetType().GetProperty(propertyName)?.GetValue(PageState.Alias, null).ToString();
break;
case "PageState:Site":
propertyValue = PageState.Site.GetType().GetProperty(propertyName)?.GetValue(PageState.Site, null).ToString();
break;
case "PageState:Page":
propertyValue = PageState.Page.GetType().GetProperty(propertyName)?.GetValue(PageState.Page, null).ToString();
break;
case "PageState:User":
propertyValue = PageState.User?.GetType().GetProperty(propertyName)?.GetValue(PageState.User, null).ToString();
break;
case "PageState:Route":
propertyValue = PageState.Route.GetType().GetProperty(propertyName)?.GetValue(PageState.Route, null).ToString();
break;
default:
if (obj != null && obj.GetType().Name == objectName)
{
propertyValue = obj.GetType().GetProperty(propertyName)?.GetValue(obj, null).ToString();
}
break;
}
if (propertyValue != null)
{
content = content.Replace("[" + token + "]", propertyValue);
}
}
}
}
return content;
}
// logging methods
public async Task Log(Alias alias, LogLevel level, string function, Exception exception, string message, params object[] args)
{

View File

@ -1,10 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
<OutputType>Exe</OutputType>
<Configurations>Debug;Release</Configurations>
<Version>5.1.2</Version>
<Version>6.1.1</Version>
<Product>Oqtane</Product>
<Authors>Shaun Walker</Authors>
<Company>.NET Foundation</Company>
@ -12,7 +12,7 @@
<Copyright>.NET Foundation</Copyright>
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v5.1.2</PackageReleaseNotes>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.1</PackageReleaseNotes>
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
<RootNamespace>Oqtane</RootNamespace>
@ -22,11 +22,10 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.5" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="8.0.5" />
<PackageReference Include="Microsoft.Extensions.Localization" Version="8.0.5" />
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Localization" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.3" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="9.0.3" />
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.3" />
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.3" />
</ItemGroup>
<ItemGroup>

View File

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.Compression;
@ -13,13 +12,13 @@ using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Microsoft.AspNetCore.Localization;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.JSInterop;
using Oqtane.Documentation;
using Oqtane.Models;
using Oqtane.Modules;
using Oqtane.Services;
using Oqtane.Shared;
using Oqtane.UI;
namespace Oqtane.Client
@ -258,7 +257,7 @@ namespace Oqtane.Client
var jsRuntime = serviceProvider.GetRequiredService<IJSRuntime>();
var interop = new Interop(jsRuntime);
var localizationCookie = await interop.GetCookie(CookieRequestCultureProvider.DefaultCookieName);
var culture = CookieRequestCultureProvider.ParseCookieValue(localizationCookie)?.UICultures?[0].Value;
var culture = CookieRequestCultureProvider.ParseCookieValue(localizationCookie)?.UICulture.Name;
var localizationService = serviceProvider.GetRequiredService<ILocalizationService>();
var cultures = await localizationService.GetCulturesAsync(false);

View File

@ -8,20 +8,20 @@
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Oqtane.Client": {
"Oqtane": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "http://localhost:44358/"
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@ -183,4 +183,13 @@
<data name="Template" xml:space="preserve">
<value>Select a site template</value>
</data>
<data name="Message.Username.Invalid" xml:space="preserve">
<value>The Username Provided Does Not Meet The System Requirement, It Can Only Contains Letters Or Digits.</value>
</data>
<data name="Name.Text" xml:space="preserve">
<value>Full Name:</value>
</data>
<data name="Name.HelpText" xml:space="preserve">
<value>Provide the full name of the host user</value>
</data>
</root>

View File

@ -150,14 +150,11 @@
<data name="Name.HelpText" xml:space="preserve">
<value>Enter the folder name</value>
</data>
<data name="Permissions.HelpText" xml:space="preserve">
<value>Select the permissions you want for the folder</value>
</data>
<data name="Parent.Text" xml:space="preserve">
<value>Parent: </value>
</data>
<data name="Permissions.Text" xml:space="preserve">
<value>Permissions: </value>
<data name="Permissions.Heading" xml:space="preserve">
<value>Permissions</value>
</data>
<data name="DeleteFolder.Header" xml:space="preserve">
<value>Delete Folder</value>
@ -178,7 +175,7 @@
<value>Capacity:</value>
</data>
<data name="ImageSizes.HelpText" xml:space="preserve">
<value>Enter a list of image sizes which can be generated dynamically from uploaded images (ie. 200x200,400x400). Use * to indicate the folder supports all image sizes.</value>
<value>Optionally enter a list of image sizes which can be generated dynamically from uploaded images (ie. 200x200,400x400). Use * to indicate the folder supports all image sizes (not recommended).</value>
</data>
<data name="ImageSizes.Text" xml:space="preserve">
<value>Image Sizes:</value>
@ -198,4 +195,13 @@
<data name="Message.Folder.Duplicate" xml:space="preserve">
<value>Folder Name Specified Already Exists In Parent</value>
</data>
<data name="Settings.Heading" xml:space="preserve">
<value>Settings</value>
</data>
<data name="CacheControl.Text" xml:space="preserve">
<value>Caching:</value>
</data>
<data name="CacheControl.HelpText" xml:space="preserve">
<value>Optionally provide a Cache-Control directive for this folder. For example 'public, max-age=60' indicates that files in this folder should be cached for 60 seconds. Please note that when caching is enabled, changes to files will not be immediately reflected in the UI.</value>
</data>
</root>

View File

@ -165,4 +165,31 @@
<data name="UploadFiles.Text" xml:space="preserve">
<value>Upload Files</value>
</data>
<data name="Files.Heading" xml:space="preserve">
<value>Files</value>
</data>
<data name="ImageExtensions.Text" xml:space="preserve">
<value>Image Extensions:</value>
</data>
<data name="ImageExtensions.HelpText" xml:space="preserve">
<value>Enter a comma separated list of image file extensions</value>
</data>
<data name="UploadableFileExtensions.Text" xml:space="preserve">
<value>Uploadable File Extensions:</value>
</data>
<data name="UploadableFileExtensions.HelpText" xml:space="preserve">
<value>Enter a comma separated list of uploadable file extensions</value>
</data>
<data name="MaxChunkSize.Text" xml:space="preserve">
<value>Max Upload Chunk Size (MB):</value>
</data>
<data name="MaxChunkSize.HelpText" xml:space="preserve">
<value>Files are split into chunks to streamline the upload process. Specify the maximum chunk size in MB (note that higher chunk sizes should only be used on faster networks).</value>
</data>
<data name="Success.SaveSiteSettings" xml:space="preserve">
<value>Settings Saved Successfully</value>
</data>
<data name="Error.SaveSiteSettings" xml:space="preserve">
<value>Error Saving Settings</value>
</data>
</root>

View File

@ -144,8 +144,8 @@
<data name="Month" xml:space="preserve">
<value>Month(s)</value>
</data>
<data name="ViewJobs.Text" xml:space="preserve">
<value>View Logs</value>
<data name="ViewLogs.Text" xml:space="preserve">
<value>View All Logs</value>
</data>
<data name="Frequency" xml:space="preserve">
<value>Frequency</value>
@ -180,9 +180,6 @@
<data name="Once" xml:space="preserve">
<value>Execute Once</value>
</data>
<data name="Message.NoJobs" xml:space="preserve">
<value>Please Note That After An Initial Installation You Must &lt;a href={0}&gt;Restart&lt;/a&gt; The Application In Order To Activate The Default Scheduled Jobs.</value>
</data>
<data name="Refresh.Text" xml:space="preserve">
<value>Refresh</value>
</data>

View File

@ -132,4 +132,7 @@
<data name="Failed" xml:space="preserve">
<value>Failed</value>
</data>
<data name="Refresh" xml:space="preserve">
<value>Refresh</value>
</data>
</root>

View File

@ -228,18 +228,6 @@
<data name="View License" xml:space="preserve">
<value>View License</value>
</data>
<data name="Error.Validate" xml:space="preserve">
<value>Error Validating Package</value>
</data>
<data name="Message.Download" xml:space="preserve">
<value>Package Version Has Been Verified. Please Select The Download Button To Obtain The Package.</value>
</data>
<data name="Message.Validate" xml:space="preserve">
<value>This Package Version Has Not Been Registered In The Oqtane Marketplace Or You Do Not Have The Right To Use It From This Installation</value>
</data>
<data name="Validate" xml:space="preserve">
<value>Validate</value>
</data>
<data name="Browse" xml:space="preserve">
<value>Browse</value>
</data>

View File

@ -267,4 +267,4 @@
<data name="ExpiryDate.Text" xml:space="preserve">
<value>Expiry Date: </value>
</data>
</root>
</root>

View File

@ -169,7 +169,7 @@
<value>Select whether the page is part of the site navigation or hidden</value>
</data>
<data name="UrlPath.HelpText" xml:space="preserve">
<value>Optionally enter a url path for this page (ie. home ). If you do not provide a url path, the page name will be used. If the page is intended to be the root path specify '/'.</value>
<value>Optionally enter a url path for this page (ie. home ). If you do not provide a url path, the page name will be used. Please note that spaces and punctuation will be replaced by a dash. If the page is intended to be the root path specify '/'.</value>
</data>
<data name="Redirect.HelpText" xml:space="preserve">
<value>Optionally enter a url which this page should redirect to when a user navigates to it</value>
@ -297,4 +297,10 @@
<data name="ExpiryDate.Text" xml:space="preserve">
<value>Expiry Date: </value>
</data>
<data name="PersonalizedUrlPath.Text" xml:space="preserve">
<value>Url Path:</value>
</data>
<data name="PersonalizedUrlPath.HelpText" xml:space="preserve">
<value>Provide a url path for your personalized page. Please note that spaces and punctuation will be replaced by a dash.</value>
</data>
</root>

View File

@ -138,4 +138,7 @@
<data name="EditPage.Text" xml:space="preserve">
<value>Edit</value>
</data>
<data name="Error.Page.Load" xml:space="preserve">
<value>Error Loading Pages</value>
</data>
</root>

View File

@ -195,4 +195,25 @@
<data name="Modules.Heading" xml:space="preserve">
<value>Modules</value>
</data>
<data name="Message.Page.Restore" xml:space="preserve">
<value>You Cannot Restore A Page If Its Parent Is Deleted</value>
</data>
<data name="Success.Page.Restore" xml:space="preserve">
<value>Page Restored Successfully</value>
</data>
<data name="Success.Page.Delete" xml:space="preserve">
<value>Page Deleted Successfully</value>
</data>
<data name="Success.Pages.Deleted" xml:space="preserve">
<value>All Pages Deleted Successfully</value>
</data>
<data name="Success.Module.Restore" xml:space="preserve">
<value>Module Restored Successfully</value>
</data>
<data name="Success.Module.Delete" xml:space="preserve">
<value>Module Deleted Successfully</value>
</data>
<data name="Success.Modules.Delete" xml:space="preserve">
<value>All Modules Deleted Successfully</value>
</data>
</root>

View File

@ -0,0 +1,180 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Enabled.Text" xml:space="preserve">
<value>Enabled? </value>
</data>
<data name="Enabled.HelpText" xml:space="preserve">
<value>Specify if search indexing is enabled</value>
</data>
<data name="LastIndexedOn.Text" xml:space="preserve">
<value>Last Indexed: </value>
</data>
<data name="LastIndexedOn.HelpText" xml:space="preserve">
<value>The date/time which the site was last indexed on</value>
</data>
<data name="IgnorePages.Text" xml:space="preserve">
<value>Ignore Pages: </value>
</data>
<data name="IgnorePages.HelpText" xml:space="preserve">
<value>Comma delimited list of pages which should be ignored (based on page path)</value>
</data>
<data name="IgnoreEntities.Text" xml:space="preserve">
<value>Ignore Entities: </value>
</data>
<data name="IgnoreEntities.HelpText" xml:space="preserve">
<value>Comma delimited list of entities which should be ignored. By default File entities are ignored.</value>
</data>
<data name="MinimumWordLength.Text" xml:space="preserve">
<value>Word Length: </value>
</data>
<data name="MinimumWordLength.HelpText" xml:space="preserve">
<value>Minimum length of a word to be indexed</value>
</data>
<data name="IgnoreWords.Text" xml:space="preserve">
<value>Ignore Words: </value>
</data>
<data name="IgnoreWords.HelpText" xml:space="preserve">
<value>Comma delimited list of words which should be ignored</value>
</data>
<data name="Success.Save" xml:space="preserve">
<value>Search Settings Saved Successfully. You Will Need Reindex For Your Changes To Be Reflected In The Search Results.</value>
</data>
<data name="Error.Save" xml:space="preserve">
<value>Error Saving Search Settings</value>
</data>
<data name="SearchProvider.HelpText" xml:space="preserve">
<value>Specify the search provider for this site</value>
</data>
<data name="SearchProvider.Text" xml:space="preserve">
<value>Search Provider:</value>
</data>
<data name="Message.Reindex" xml:space="preserve">
<value>The search index will be rebuilt for this site. Please be patient during the reindexing process.</value>
</data>
<data name="Reindex.Text" xml:space="preserve">
<value>Reindex</value>
</data>
<data name="Reindex.Header" xml:space="preserve">
<value>Reindex</value>
</data>
<data name="Reindex.Message" xml:space="preserve">
<value>Are You Sure You Wish To Reindex Search Content?</value>
</data>
</root>

View File

@ -0,0 +1,132 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="NoCriteria" xml:space="preserve">
<value>Please Enter Some Search Criteria</value>
</data>
<data name="NoResult" xml:space="preserve">
<value>No Content Matches The Criteria Provided</value>
</data>
<data name="SearchLabel" xml:space="preserve">
<value>Search:</value>
</data>
<data name="SearchPlaceholder" xml:space="preserve">
<value>Search</value>
</data>
</root>

View File

@ -0,0 +1,180 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Ascending" xml:space="preserve">
<value>Ascending</value>
</data>
<data name="BodyLength.HelpText" xml:space="preserve">
<value>The number of characters displayed for each search result summary. The default is 255 characters.</value>
</data>
<data name="BodyLength.Text" xml:space="preserve">
<value>Body Size:</value>
</data>
<data name="DateRange.HelpText" xml:space="preserve">
<value>Enter the date range for search results. The default includes all content.</value>
</data>
<data name="DateRange.Text" xml:space="preserve">
<value>Date Range:</value>
</data>
<data name="Descending" xml:space="preserve">
<value>Descending</value>
</data>
<data name="ExcludeEntities.HelpText" xml:space="preserve">
<value>Comma delimited list of entities to exclude from search results. By default no entities will be excluded.</value>
</data>
<data name="ExcludeEntities.Text" xml:space="preserve">
<value>Exlude Entities:</value>
</data>
<data name="IncludeEntities.HelpText" xml:space="preserve">
<value>Comma delimited list of entities to include in the search results. By default all entities will be included.</value>
</data>
<data name="IncludeEntities.Text" xml:space="preserve">
<value>Include Entities:</value>
</data>
<data name="LastModified" xml:space="preserve">
<value>LastModified</value>
</data>
<data name="PageSize.HelpText" xml:space="preserve">
<value>The maximum number of search results to retrieve. The default is unlimited.</value>
</data>
<data name="PageSize.Text" xml:space="preserve">
<value>Page Size:</value>
</data>
<data name="Relevance" xml:space="preserve">
<value>Relevance</value>
</data>
<data name="SortField.HelpText" xml:space="preserve">
<value>Specify the default sort field</value>
</data>
<data name="SortField.Text" xml:space="preserve">
<value>Sort By:</value>
</data>
<data name="SortOrder.HelpText" xml:space="preserve">
<value>Specify the default sort order</value>
</data>
<data name="SortOrder.Text" xml:space="preserve">
<value>Sort Order:</value>
</data>
<data name="Title" xml:space="preserve">
<value>Title</value>
</data>
<data name="To" xml:space="preserve">
<value>To</value>
</data>
</root>

View File

@ -163,7 +163,7 @@
<value>Enter the site name</value>
</data>
<data name="Tenant.HelpText" xml:space="preserve">
<value>The name of the database used for the site</value>
<value>The name of the database used for the site. Note that this is not the physical database name but rather the tenant name which is used within the framework to identify a database.</value>
</data>
<data name="Aliases.HelpText" xml:space="preserve">
<value>The urls for the site. This can include domain names (ie. domain.com), subdomains (ie. sub.domain.com) or virtual folders (ie. domain.com/folder).</value>
@ -307,7 +307,7 @@
<value>Type:</value>
</data>
<data name="ConnectionString.HelpText" xml:space="preserve">
<value>The connection information for the database</value>
<value>The name of the connection string in appsettings.json which will be used to connect to the database</value>
</data>
<data name="Database.HelpText" xml:space="preserve">
<value>The type of database</value>
@ -349,7 +349,7 @@
<value>Relay Configured?</value>
</data>
<data name="SiteMap.HelpText" xml:space="preserve">
<value>The site map url for this site which can be submitted to search engines for indexing</value>
<value>The site map url for this site which can be submitted to search engines for indexing. The sitemap is cached for 5 minutes and the cache can be manually cleared.</value>
</data>
<data name="SiteMap.Text" xml:space="preserve">
<value>Site Map:</value>
@ -402,21 +402,6 @@
<data name="Retention.Text" xml:space="preserve">
<value>Retention (Days):</value>
</data>
<data name="FileExtensions.Heading" xml:space="preserve">
<value>File Extensions</value>
</data>
<data name="ImageExtensions.HelpText" xml:space="preserve">
<value>Enter a comma separated list of image file extensions</value>
</data>
<data name="ImageExtensions.Text" xml:space="preserve">
<value>Image Extensions:</value>
</data>
<data name="UploadableFileExtensions.HelpText" xml:space="preserve">
<value>Enter a comma separated list of uploadable file extensions</value>
</data>
<data name="UploadableFileExtensions.Text" xml:space="preserve">
<value>Uploadable File Extensions:</value>
</data>
<data name="HybridEnabled.HelpText" xml:space="preserve">
<value>Specifies if the site can be integrated with an external .NET MAUI hybrid application</value>
</data>
@ -429,4 +414,37 @@
<data name="Runtime.Text" xml:space="preserve">
<value>Interactivity:</value>
</data>
<data name="TextEditor.HelpText" xml:space="preserve">
<value>Select the text editor for the site</value>
</data>
<data name="TextEditor.Text" xml:space="preserve">
<value>Text Editor:</value>
</data>
<data name="Functionality" xml:space="preserve">
<value>Functionality</value>
</data>
<data name="System" xml:space="preserve">
<value>System</value>
</data>
<data name="CookieConsent.HelpText" xml:space="preserve">
<value>Specify if cookie consent is enabled on this site. Please note this option must be used in conjunction with a Theme which supports cookie consent.</value>
</data>
<data name="CookieConsent.Text" xml:space="preserve">
<value>Cookie Consent:</value>
</data>
<data name="OptIn" xml:space="preserve">
<value>Opt-In (GDPR)</value>
</data>
<data name="OptOut" xml:space="preserve">
<value>Opt-Out (CCPA)</value>
</data>
<data name="Theme.Heading" xml:space="preserve">
<value>Theme</value>
</data>
<data name="SiteMap.EvictCache" xml:space="preserve">
<value>Clear Cache</value>
</data>
<data name="Success.SiteMap.CacheEvicted" xml:space="preserve">
<value>Site Map Cache Cleared</value>
</data>
</root>

View File

@ -187,7 +187,7 @@
<value>Select the database for the site</value>
</data>
<data name="TenantName.HelpText" xml:space="preserve">
<value>Enter the name for the database</value>
<value>Enter the name for the database. Note that this will be the tenant name which is used within the framework to identify the database.</value>
</data>
<data name="DatabaseType.HelpText" xml:space="preserve">
<value>Select the database type</value>

View File

@ -117,8 +117,8 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Access.ApiFramework" xml:space="preserve">
<value>Access Swagger API</value>
<data name="Swagger" xml:space="preserve">
<value>Swagger UI</value>
</data>
<data name="FrameworkVersion.HelpText" xml:space="preserve">
<value>Framework Version</value>
@ -220,10 +220,10 @@
<value>You Have Been Successfully Registered For Updates</value>
</data>
<data name="PackageManager.HelpText" xml:space="preserve">
<value>Specify The Package Manager Service For Installing Modules, Themes, And Translations. If This Field Is Blank It Means The Package Manager Service Is Disabled For This Installation.</value>
<value>Specify The Url Of The Package Manager Service For Installing Modules, Themes, And Translations. If This Field Is Blank It Means The Package Manager Service Is Disabled For This Installation.</value>
</data>
<data name="PackageManager.Text" xml:space="preserve">
<value>Package Manager:</value>
<value>Package Manager Url:</value>
</data>
<data name="Swagger.HelpText" xml:space="preserve">
<value>Specify If Swagger Is Enabled For Your Server API</value>
@ -294,4 +294,19 @@
<data name="Process.Text" xml:space="preserve">
<value>Process: </value>
</data>
<data name="PackageManagerEmail.Text" xml:space="preserve">
<value>Package Manager Email:</value>
</data>
<data name="PackageManagerEmail.HelpText" xml:space="preserve">
<value>Specify The Email Address Of The User Account Used For Interacting With The Package Manager Service. This Account Is Used For Managing Packages Across Multiple Installations.</value>
</data>
<data name="CacheControl.Text" xml:space="preserve">
<value>Static Asset Caching:</value>
</data>
<data name="CacheControl.HelpText" xml:space="preserve">
<value>Provide a Cache-Control directive for static assets. For example 'public, max-age=60' indicates that static assets should be cached for 60 seconds. A blank value indicates caching is not enabled.</value>
</data>
<data name="Endpoints" xml:space="preserve">
<value>API Endpoints</value>
</data>
</root>

View File

@ -180,16 +180,4 @@
<data name="View License" xml:space="preserve">
<value>View License</value>
</data>
<data name="Error.Validate" xml:space="preserve">
<value>Error Validating Package</value>
</data>
<data name="Message.Download" xml:space="preserve">
<value>Package Version Has Been Verified. Please Select The Download Button To Obtain The Package.</value>
</data>
<data name="Message.Validate" xml:space="preserve">
<value>This Package Version Has Not Been Registered In The Oqtane Marketplace Or You Do Not Have The Right To Use It From This Installation</value>
</data>
<data name="Validate" xml:space="preserve">
<value>Validate</value>
</data>
</root>

View File

@ -146,7 +146,7 @@
</data>
<data name="InstallTheme.Text" xml:space="preserve">
<value>Install Theme</value>
</data>
</data>
<data name="ViewTheme.Text" xml:space="preserve">
<value>View</value>
</data>
@ -156,4 +156,7 @@
<data name="Enabled" xml:space="preserve">
<value>Enabled?</value>
</data>
<data name="Assign" xml:space="preserve">
<value>Assign</value>
</data>
</root>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
@ -150,4 +150,7 @@
<data name="Localhost.Text" xml:space="preserve">
<value>You Cannot Perform A System Update In A Development Environment</value>
</data>
<data name="Disclaimer.Text" xml:space="preserve">
<value>Please Note That The System Update Capability Is A Simplified Upgrade Process Intended For Small To Medium Sized Installations. For Larger Enterprise Installations You Will Want To Use A Manual Upgrade Process. Also Note That The System Update Capability Is Not Recommended When Using Microsoft Azure Due To Environmental Limitations.</value>
</data>
</root>

View File

@ -162,4 +162,10 @@
<data name="Edit.Text" xml:space="preserve">
<value>Edit</value>
</data>
<data name="Retention.Text" xml:space="preserve">
<value>Retention (Days):</value>
</data>
<data name="Retention.HelpText" xml:space="preserve">
<value>Number of days of broken urls to retain</value>
</data>
</root>

View File

@ -243,4 +243,7 @@
<data name="NoNotificationsSent.Text" xml:space="preserve">
<value>No notifications have been sent</value>
</data>
<data name="Logout Everywhere" xml:space="preserve">
<value>Logout Everywhere</value>
</data>
</root>

View File

@ -117,12 +117,6 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Error.User.AddCheckPass" xml:space="preserve">
<value>Error Adding User. Please Ensure Password Meets Complexity Requirements And Username And Email Is Not Already In Use.</value>
</data>
<data name="Message.Password.NoMatch" xml:space="preserve">
<value>Passwords Entered Do Not Match</value>
</data>
<data name="Error.User.Add" xml:space="preserve">
<value>Error Adding User</value>
</data>
@ -133,17 +127,11 @@
<value>Identity</value>
</data>
<data name="Message.Required.ProfileInfo" xml:space="preserve">
<value>You Must Provide A Username, Password, Email Address And All Required Profile Information</value>
<value>You Must Provide A Username, Email Address And All Required Profile Information</value>
</data>
<data name="Message.Username.Exists" xml:space="preserve">
<value>Username Already Exists</value>
</data>
<data name="Confirm.HelpText" xml:space="preserve">
<value>Please enter the password again to confirm it matches with the value above</value>
</data>
<data name="Confirm.Text" xml:space="preserve">
<value>Confirm Password:</value>
</data>
<data name="DisplayName.HelpText" xml:space="preserve">
<value>The full name of the user</value>
</data>
@ -156,21 +144,12 @@
<data name="Email.Text" xml:space="preserve">
<value>Email:</value>
</data>
<data name="Password.HelpText" xml:space="preserve">
<value>The user's password. Please choose a password which is sufficiently secure.</value>
</data>
<data name="Password.Text" xml:space="preserve">
<value>Password:</value>
</data>
<data name="Username.HelpText" xml:space="preserve">
<value>A unique username for a user. Note that this field can not be modified once it is saved.</value>
</data>
<data name="Username.Text" xml:space="preserve">
<value>Username:</value>
</data>
<data name="Password.Placeholder" xml:space="preserve">
<value>Password</value>
</data>
<data name="Notify.HelpText" xml:space="preserve">
<value>Indicate if new users should receive an email notification</value>
</data>

View File

@ -195,4 +195,19 @@
<data name="LastLogin.Text" xml:space="preserve">
<value>Last Login:</value>
</data>
<data name="DeleteUser.Header" xml:space="preserve">
<value>Delete User</value>
</data>
<data name="DeleteUser.Text" xml:space="preserve">
<value>Delete</value>
</data>
<data name="DeleteUser.Message" xml:space="preserve">
<value>Are You Sure You Wish To Permanently Delete This User?</value>
</data>
<data name="Impersonate" xml:space="preserve">
<value>Impersonate</value>
</data>
<data name="Error.User.Impersonate" xml:space="preserve">
<value>Unable To Impersonate User</value>
</data>
</root>

View File

@ -385,10 +385,22 @@
<value>Parameters:</value>
</data>
<data name="RoleClaimType.HelpText" xml:space="preserve">
<value>Optionally provide the type name of the role claim provided by the identity provider. These roles will be used in addition to any internal user roles assigned within the site.</value>
<value>Optionally provide the type name of the roles claim provided by the identity provider (the standard default is 'roles'). If role names from the identity provider do not exactly match your site role names, please use the Role Claim Mappings.</value>
</data>
<data name="RoleClaimType.Text" xml:space="preserve">
<value>Role Claim:</value>
<value>Roles Claim:</value>
</data>
<data name="RoleClaimMappings.HelpText" xml:space="preserve">
<value>Optionally provide a comma delimited list of role names provided by the identity provider, as well as mappings to your site roles. For example if the identity provider includes an 'Admin' role name and you want it to map to the 'Administrators' site role you should specify 'Admin:Administrators'.</value>
</data>
<data name="RoleClaimMappings.Text" xml:space="preserve">
<value>Role Claim Mappings:</value>
</data>
<data name="SynchronizeRoles.HelpText" xml:space="preserve">
<value>This option will add or remove role assignments so that the site roles exactly match the roles provided by the identity provider for a user</value>
</data>
<data name="SynchronizeRoles.Text" xml:space="preserve">
<value>Synchronize Roles?</value>
</data>
<data name="ProfileClaimTypes.HelpText" xml:space="preserve">
<value>Optionally provide a comma delimited list of user profile claim type names provided by the identity provider, as well as mappings to your user profile definition. For example if the identity provider includes a 'given_name' claim and you have a 'FirstName' user profile definition you should specify 'given_name:FirstName'.</value>
@ -459,13 +471,46 @@
<data name="ReviewClaims.Text" xml:space="preserve">
<value>Review Claims?</value>
</data>
<data name="ReviewClaims.HelpText" xml:space="preserve">
<data name="ReviewClaims.HelpText" xml:space="preserve">
<value>This option will record the full list of Claims returned by the Provider in the Event Log. It should only be used for testing purposes. External Login will be restricted when this option is enabled.</value>
</data>
<data name="NameClaimType.HelpText" xml:space="preserve">
<value>Optionally specify the type name of the user's name claim provided by the identity provider. The typical value is 'name'.</value>
</data>
<data name="NameClaimType.Text" xml:space="preserve">
<data name="NameClaimType.Text" xml:space="preserve">
<value>Name Claim:</value>
</data>
<data name="Provider.HelpText" xml:space="preserve">
<value>Select the external login provider</value>
</data>
<data name="Provider.Text" xml:space="preserve">
<value>Provider:</value>
</data>
<data name="Info" xml:space="preserve">
<value>Info</value>
</data>
<data name="OAuth2" xml:space="preserve">
<value>OAuth 2.0</value>
</data>
<data name="OIDC" xml:space="preserve">
<value>OpenID Connect (OIDC)</value>
</data>
<data name="SaveTokens.Text" xml:space="preserve">
<value>Save Tokens?</value>
</data>
<data name="SaveTokens.HelpText" xml:space="preserve">
<value>Specify whether access and refresh tokens should be saved after a successful login. The default is false to reduce the size of the authentication cookie.</value>
</data>
<data name="Success.DeleteUser" xml:space="preserve">
<value>User Deleted Successfully</value>
</data>
<data name="Error.DeleteUser" xml:space="preserve">
<value>Error Deleting User</value>
</data>
<data name="LogoutEverywhere.Text" xml:space="preserve">
<value>Logout Everywhere?</value>
</data>
<data name="LogoutEverywhere.HelpText" xml:space="preserve">
<value>Do you want users to be logged out of every active session on any device, or only their current session?</value>
</data>
</root>

View File

@ -127,7 +127,7 @@
<value>Error Loading Files</value>
</data>
<data name="Error.File.Upload" xml:space="preserve">
<value>File Upload Failed Or Is Still In Progress</value>
<value>File Upload Failed</value>
</data>
<data name="Message.File.NotSelected" xml:space="preserve">
<value>You Have Not Selected A File To Upload</value>

View File

@ -0,0 +1,174 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="AllowFileManagement.HelpText" xml:space="preserve">
<value>Specify if editors can upload and insert images</value>
</data>
<data name="AllowFileManagement.Text" xml:space="preserve">
<value>Insert Images?</value>
</data>
<data name="AllowRawHtml.HelpText" xml:space="preserve">
<value>Specify if editors can use the Raw HTML Editor</value>
</data>
<data name="AllowRawHtml.Text" xml:space="preserve">
<value>Raw HTML Editor?</value>
</data>
<data name="AllowRichText.HelpText" xml:space="preserve">
<value>Specify if editors can use the Rich Text Editor</value>
</data>
<data name="AllowRichText.Text" xml:space="preserve">
<value>Rich Text Editor? </value>
</data>
<data name="Close" xml:space="preserve">
<value>Close</value>
</data>
<data name="DebugLevel.HelpText" xml:space="preserve">
<value>Specify the Debug Level</value>
</data>
<data name="DebugLevel.Text" xml:space="preserve">
<value>Debug Level:</value>
</data>
<data name="InsertImage" xml:space="preserve">
<value>Insert Image</value>
</data>
<data name="Message.Require.Image" xml:space="preserve">
<value>You Must Select An Image To Insert</value>
</data>
<data name="Placeholder" xml:space="preserve">
<value>Enter Your Content...</value>
</data>
<data name="SaveSettings" xml:space="preserve">
<value>Save Settings</value>
</data>
<data name="Settings" xml:space="preserve">
<value>Settings</value>
</data>
<data name="Theme.HelpText" xml:space="preserve">
<value>Specify the Rich Text Editor's theme</value>
</data>
<data name="Theme.Text" xml:space="preserve">
<value>Theme:</value>
</data>
<data name="ToolbarContent.HelpText" xml:space="preserve">
<value>Specify any toolbar content to customize the Rich Text Editor</value>
</data>
<data name="ToolbarContent.Text" xml:space="preserve">
<value>Toolbar Content:</value>
</data>
</root>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
@ -117,16 +117,10 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="AllowFileManagement.HelpText" xml:space="preserve">
<value>Specify If Editors Can Upload and Select Files</value>
<data name="DynamicTokens.HelpText" xml:space="preserve">
<value>Do you wish to allow tokens to be dynamically replaced? Please note that this will affect the performance of your site.</value>
</data>
<data name="AllowFileManagement.Text" xml:space="preserve">
<value>Allow File Management: </value>
</data>
<data name="AllowRawHtml.HelpText" xml:space="preserve">
<value>Specify If Editors Can Enter Raw HTML</value>
</data>
<data name="AllowRawHtml.Text" xml:space="preserve">
<value>Allow Raw HTML:</value>
<data name="DynamicTokens.Text" xml:space="preserve">
<value>Dynamic Tokens?</value>
</data>
</root>

View File

@ -213,6 +213,9 @@
<data name="Reset" xml:space="preserve">
<value>Reset</value>
</data>
<data name="Search Settings" xml:space="preserve">
<value>Search Settings</value>
</data>
<data name="Search" xml:space="preserve">
<value>Search</value>
</data>
@ -424,7 +427,7 @@
<value>At Least One Uppercase Letter</value>
</data>
<data name="Password.ValidationCriteria" xml:space="preserve">
<value>Passwords Must Have A Minimum Length Of {0} Characters, Including At Least {1} Unique Character(s), {2}{3}{4}{5} To Satisfy Password Compexity Requirements For This Site.</value>
<value>Passwords Must Have A Minimum Length Of {0} Characters, Including At Least {1} Unique Character(s), {2}{3}{4}{5} To Satisfy Password Complexity Requirements For This Site.</value>
</data>
<data name="ProfileInvalid" xml:space="preserve">
<value>{0} Is Not Valid</value>
@ -459,4 +462,19 @@
<data name="Enabled" xml:space="preserve">
<value>Enabled</value>
</data>
<data name="Module" xml:space="preserve">
<value>Module</value>
</data>
<data name="Page" xml:space="preserve">
<value>Page</value>
</data>
<data name="Site" xml:space="preserve">
<value>Site</value>
</data>
<data name="User" xml:space="preserve">
<value>User</value>
</data>
<data name="Path" xml:space="preserve">
<value>Path</value>
</data>
</root>

View File

@ -0,0 +1,129 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Confirm" xml:space="preserve">
<value>Confirm</value>
</data>
<data name="ConsentNotice" xml:space="preserve">
<value>I agree to use cookies to provide the best possible user experience for this site. I understand that I can change these preferences at any time.</value>
</data>
<data name="Privacy" xml:space="preserve">
<value>Privacy</value>
</data>
</root>

View File

@ -117,16 +117,10 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="InsertImage" xml:space="preserve">
<value>Insert Image</value>
<data name="Search" xml:space="preserve">
<value>Search</value>
</data>
<data name="Close" xml:space="preserve">
<value>Close</value>
</data>
<data name="Message.Require.Image" xml:space="preserve">
<value>You Must Select An Image To Insert</value>
</data>
<data name="Placeholder" xml:space="preserve">
<value>Enter Your Content...</value>
<data name="SearchPlaceHolder" xml:space="preserve">
<value>Search</value>
</data>
</root>

View File

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

View File

@ -0,0 +1,47 @@
using Oqtane.Models;
using System.Threading.Tasks;
using System.Net.Http;
using System;
using Oqtane.Documentation;
using Oqtane.Shared;
using System.Globalization;
namespace Oqtane.Services
{
/// <inheritdoc cref="ICookieConsentService" />
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
public class CookieConsentService : ServiceBase, ICookieConsentService
{
public CookieConsentService(HttpClient http, SiteState siteState) : base(http, siteState) { }
private string ApiUrl => CreateApiUrl("CookieConsent");
public async Task<bool> IsActionedAsync()
{
return await GetJsonAsync<bool>($"{ApiUrl}/IsActioned");
}
public async Task<bool> CanTrackAsync(bool optOut)
{
return await GetJsonAsync<bool>($"{ApiUrl}/CanTrack?optout=" + optOut);
}
public async Task<string> CreateActionedCookieAsync()
{
var cookie = await GetStringAsync($"{ApiUrl}/CreateActionedCookie");
return cookie ?? string.Empty;
}
public async Task<string> CreateConsentCookieAsync()
{
var cookie = await GetStringAsync($"{ApiUrl}/CreateConsentCookie");
return cookie ?? string.Empty;
}
public async Task<string> WithdrawConsentCookieAsync()
{
var cookie = await GetStringAsync($"{ApiUrl}/WithdrawConsentCookie");
return cookie ?? string.Empty;
}
}
}

View File

@ -75,5 +75,10 @@ namespace Oqtane.Services
{
return await GetByteArrayAsync($"{Apiurl}/download/{fileId}");
}
public async Task UnzipFileAsync(int fileId)
{
await PutAsync($"{Apiurl}/unzip/{fileId}");
}
}
}

View File

@ -56,10 +56,5 @@ namespace Oqtane.Services
{
await PostAsync($"{ApiUrl}/restart");
}
public async Task RegisterAsync(string email)
{
await PostJsonAsync($"{ApiUrl}/register?email={WebUtility.UrlEncode(email)}", true);
}
}
}

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