Compare commits

...

213 Commits

Author SHA1 Message Date
be1d124e78 Merge pull request #3322 from oqtane/master
Merge pull request #3321 from oqtane/dev
2023-09-25 15:55:54 -04:00
6fabf84d05 Merge pull request #3321 from oqtane/dev
4.0.4 release
2023-09-25 15:55:37 -04:00
3299275da2 Merge pull request #3320 from sbwalker/dev
resolve cookie configuration
2023-09-25 15:10:50 -04:00
5539243bf3 resolve cookie configuration 2023-09-25 15:10:36 -04:00
c3d330f500 Merge pull request #3319 from sbwalker/dev
refactor logic to redirect to external login provider when local site login is disabled
2023-09-25 13:53:26 -04:00
8c9e886136 refactor logic to redirect to external login provider when local site login is disabled 2023-09-25 13:53:05 -04:00
cfde9944f8 Merge pull request #3318 from sbwalker/dev
include Logout link in Control Panel for scenarios where a theme does not include a Login/Logout component
2023-09-25 12:42:59 -04:00
b35d778abe include Logout link in Control Panel for scenarios where a theme does not include a Login/Logout component 2023-09-25 12:42:39 -04:00
1e9ee81df3 Merge pull request #3317 from sbwalker/dev
updated database provider packages for 4.0.4
2023-09-25 10:01:59 -04:00
545d5730b6 database provider packages for 4.0.4 2023-09-25 09:59:07 -04:00
28e645f9ca Merge branch 'dev' of https://github.com/sbwalker/oqtane.framework into dev 2023-09-25 09:54:38 -04:00
5b5c8a4beb database provider packages for 4.0.4 2023-09-25 09:54:27 -04:00
6178be974a Merge pull request #3310 from leigh-pointer/LangSwitchPadding
upated LanguageSwitcher with end padding
2023-09-25 07:42:24 -04:00
7771c3159b upated LanguageSwitcher with end padding
When the LanguageSwitcher is dsiplayed the wsa no end padding on the button so it appeared flush with the following button.  Adding "pe-1" to the class corrects the issue
2023-09-24 18:49:50 +02:00
fc1e9c5d71 Merge pull request #3308 from sbwalker/dev
revert remaining logic which forced page themes to be a member of a site theme (4.0.0)
2023-09-23 11:47:08 -04:00
359de5b85e revert remaining logic which forced page themes to be a member of a site theme 2023-09-23 11:46:35 -04:00
68dc4904cf Merge pull request #3307 from sbwalker/dev
improve user import API
2023-09-23 11:37:43 -04:00
057fd02e26 improve user import API 2023-09-23 11:37:29 -04:00
5c86ef6682 Merge pull request #3303 from leigh-pointer/Retention-resx
Fix for  Site 'SMTP retention' is not in resource file (Issue #3301) and #3304 Users 'Import Users' resource
2023-09-23 09:12:33 -04:00
7c14bd799f Merge pull request #3305 from sbwalker/dev
improvements based on user import testing
2023-09-23 09:04:35 -04:00
edac046fcd improvements based on user import testing 2023-09-23 09:04:18 -04:00
8b23b386f7 ImportUsers Added to Resx and Razor resource key 2023-09-23 10:56:17 +02:00
e9bed59032 Fix for Site 'SMTP retention' is not in resource file (Issue #3301)
Fix for  Site 'SMTP retention' is not in resource file (Issue #3301)
2023-09-23 10:42:39 +02:00
54077be32c Merge pull request #3299 from sbwalker/dev
fix reference to DisplayName
2023-09-22 15:03:57 -04:00
30ad442dd1 fix reference to DisplayName 2023-09-22 15:03:45 -04:00
b0c677d5a7 Merge pull request #3298 from sbwalker/dev
removing href link from DisplayName as Email is now included
2023-09-22 14:44:36 -04:00
abe1b93e3c removing href link from DisplayName as Email is now included 2023-09-22 14:44:25 -04:00
fd699cbb73 Merge pull request #3293 from W6HBR/dev
Add Email column to User Manager screen
2023-09-22 14:31:56 -04:00
6f93ca11de Merge pull request #3297 from sbwalker/dev
fix issue where module migrations were not being executed on upgrade due to version not being overridden
2023-09-22 14:12:36 -04:00
916663b493 fix issue where module migrations were not being executed on upgrade due to version not being overridden 2023-09-22 14:12:18 -04:00
dbf2cddd87 Update Index.razor to include Email column. 2023-09-21 16:58:48 -07:00
d3c248cf5c Update Index.resx for Email text. 2023-09-21 16:57:01 -07:00
bd93a94bb7 Merge pull request #3292 from thabaum/patch-6
Fix typo in app.css
2023-09-21 19:42:02 -04:00
a46836e2a6 Fix typo in app.css 2023-09-21 16:34:57 -07:00
a1d88b4faa Merge pull request #3290 from sbwalker/dev
add named site option support to factory
2023-09-21 17:17:59 -04:00
29741c0ec8 add named site option support to factory 2023-09-21 17:17:46 -04:00
0b5fb92452 Merge pull request #3289 from sbwalker/dev
add support for named site options
2023-09-21 16:54:04 -04:00
65782e87c1 add support for named site options 2023-09-21 16:53:52 -04:00
6403df3330 Merge pull request #3288 from sbwalker/dev
set DefaultScheme for authentication
2023-09-21 14:45:09 -04:00
c6a8f5305a set DefaultScheme for authentication 2023-09-21 14:44:57 -04:00
d354e63267 Merge pull request #3285 from sbwalker/dev
fix localization in Profile Management
2023-09-21 10:02:36 -04:00
836b174d15 fix localization in Profile Management 2023-09-21 10:02:21 -04:00
4435f25ee6 Merge pull request #3275 from W6HBR/dev
Update to Profiles field list to include Title, Category and ViewOrder
2023-09-21 09:57:55 -04:00
4ab4bf4a3a Merge pull request #3279 from thabaum/patch-5
Updates Child Page Link Color in Theme Creator Template - Theme.css - Fixes #3278
2023-09-21 09:57:30 -04:00
d2f1d3c86c Merge pull request #3283 from sbwalker/dev
user import improvements
2023-09-21 09:37:44 -04:00
98257de005 user import improvements 2023-09-21 09:37:29 -04:00
5ea5c6ff7d Updates Child Page Link Color in Theme.css 2023-09-20 17:12:04 -07:00
441324d354 Update to Profiles field list to include Title, Category and ViewOrder
When adding additional profile fields, this enhancement makes it easier to see details of the profile fields for the purpose of field organization.
2023-09-20 14:59:44 -07:00
44f093b2fa Merge pull request #3266 from W6HBR/W6HBR-patch-1
Add password complexity requirements message to password Reset module.
2023-09-20 15:19:03 -04:00
38a16d1ae2 Merge pull request #3273 from sbwalker/dev
prepare for 4.0.4 release
2023-09-20 14:59:10 -04:00
6b9de40442 prepare for 4.0.4 release 2023-09-20 14:58:54 -04:00
d9c9eb9799 Merge pull request #3258 from leigh-pointer/ViewNotification
Removed the ReadOnly attribute in the Notifications Reply TextArea
2023-09-20 14:32:41 -04:00
e88c8eee0f Merge pull request #3271 from sbwalker/dev
fix #3231 - validate module description
2023-09-20 14:29:21 -04:00
a6ca843ced fix #3231 - validate module description 2023-09-20 14:29:06 -04:00
6967a5cc14 Merge pull request #3270 from sbwalker/dev
if site login is disabled redirect Login to external identity provider
2023-09-20 13:45:34 -04:00
e0ebf70907 if site login is disabled redirect Login to external identity provider 2023-09-20 13:45:19 -04:00
53d0202f3b Merge pull request #3268 from sbwalker/dev
prevent System Update in development environment
2023-09-20 12:34:20 -04:00
8daf38654d prevent System Update in development environment 2023-09-20 12:33:43 -04:00
17337440f2 Merge pull request #3267 from sbwalker/dev
add ability to import users
2023-09-20 10:44:05 -04:00
8e5e79a799 add ability to import users 2023-09-20 10:43:49 -04:00
5c1cf14303 Add password complexity requirements message to password Reset module.
This adds the same functionality that already exists in the UserProfile module.
2023-09-19 18:59:38 -07:00
f2ad796104 Merge pull request #3263 from sbwalker/dev
adjust error message text when adding users with duplicate email addresses
2023-09-19 09:00:11 -04:00
886df69058 adjust error message text when adding users with duplicate email addresses 2023-09-19 08:59:53 -04:00
703aa95e60 Merge pull request #3262 from sbwalker/dev
fix error when Allow User Login is set to false (ElementReference has not been configured correctly)
2023-09-19 08:49:21 -04:00
7919e14d90 fix error when Allow User Login is set to false (ElementReference has not been configured correctly) 2023-09-19 08:49:03 -04:00
c8ef191660 Merge pull request #3261 from sbwalker/dev
remove using statements added by Visual Studio
2023-09-18 09:47:52 -04:00
840b656d1c remove using statements added by Visual Studio 2023-09-18 09:47:38 -04:00
d28516b6b1 Merge pull request #3260 from sbwalker/dev
fix issue with module order in panes caused by transition from Admin to Default pane naming
2023-09-18 09:46:40 -04:00
5797bf549c fix issue with module order in panes caused by transition from Admin to Default pane naming 2023-09-18 09:46:16 -04:00
e1ea721ea9 Removed the ReadOnly attribute
The replay textarea can now be used to enter a reply to the notification.
2023-09-16 18:35:22 +02:00
5ed1284baf Merge pull request #3257 from sbwalker/dev
fix #3255 - behavior when moving pages to other parents
2023-09-15 16:23:28 -04:00
e507023a03 fix #3255 - behavior when moving pages to other parents 2023-09-15 16:23:14 -04:00
2b328b9bb2 Merge pull request #3254 from sbwalker/dev
fix #3253 - login needs to validate User.IsDeleted property
2023-09-13 10:02:27 -04:00
d155e13399 fix #3253 - login needs to validate User.IsDeleted property 2023-09-13 10:02:11 -04:00
6993d4782d Merge pull request #3244 from sbwalker/dev
improve polling in file upload to use actual file sizes to calculate duration
2023-09-08 17:16:28 -04:00
9267efce01 improve polling in file upload to use actual file sizes to calculate duration 2023-09-08 17:16:09 -04:00
8c88cec863 Merge pull request #3242 from sbwalker/dev
retain querystring parameters on url mapping redirect
2023-09-08 12:05:35 -04:00
b4b9976567 retain querystring parameters on url mapping redirect 2023-09-08 12:05:20 -04:00
3cbd820d9c Merge pull request #3240 from sbwalker/dev
fix #3236 - script injection issue
2023-09-08 11:25:40 -04:00
edd77b0222 fix #3236 - script injection issue 2023-09-08 11:25:27 -04:00
c0991df9a2 Merge pull request #3239 from sbwalker/dev
fix #3235 - </script> not being removed from Head Content
2023-09-08 11:09:46 -04:00
26921c899e fix #3235 - </script> not being removed from Head Content 2023-09-08 11:09:07 -04:00
a1e5912d37 Merge pull request #3238 from sbwalker/dev
fix #3229 - changing page path in Edot Page when invoked from Control Panel results in 404 when redirecting
2023-09-08 10:35:52 -04:00
037f1ec887 fix #3229 - changing page path in Edot Page when invoked from Control Panel results in 404 when redirecting 2023-09-08 10:35:22 -04:00
89ab44590b Merge pull request #3224 from sbwalker/dev
capitalize
2023-09-05 11:28:52 -04:00
4a20be8b34 capitalize 2023-09-05 11:28:36 -04:00
32a401ba67 Merge pull request #3223 from sbwalker/dev
display Site Guid in Site Settings
2023-09-05 11:27:30 -04:00
138c01aef8 display Site Guid in Site Settings 2023-09-05 11:27:17 -04:00
3e54bc9c77 Merge pull request #3221 from sbwalker/dev
add ability to validate and download packages
2023-09-02 14:08:34 -04:00
9966fc4651 add ability to validate and download packages 2023-09-02 14:08:21 -04:00
c9ed5ec98f Merge pull request #3220 from sbwalker/dev
improve help text for paclage name
2023-09-01 14:04:38 -04:00
1f4ae5dbfb improve help text for paclage name 2023-09-01 14:04:27 -04:00
d0d3cc8faa Merge pull request #3219 from sbwalker/dev
fix paths in Edit Page / Modules tab / Edit option
2023-09-01 13:57:26 -04:00
7513ae28f0 fix paths in Edit Page / Modules tab / Edit option 2023-09-01 13:57:14 -04:00
c151c6f55a Merge pull request #3218 from sbwalker/dev
include User Settings when calling UserService
2023-09-01 13:16:51 -04:00
d491aeeba6 include User Settings when calling UserService 2023-09-01 13:16:39 -04:00
ab93d0acea Merge pull request #3217 from sbwalker/dev
Fix #3215 - module creator creates incorrect ServerManagerType value
2023-09-01 13:03:01 -04:00
f40f3f934f Fix #3215 - module creator creates incorrect ServerManagerType value 2023-09-01 13:02:44 -04:00
4d57adb393 Merge pull request #3216 from sbwalker/dev
allow an administratot to browse to the SiteMap, handle default module panes and ordering for PageTemplates, allow trial products to be purchased
2023-09-01 12:14:24 -04:00
e91db18677 allow an administratot to browse to the SiteMap, handle default module panes and ordering for PageTemplates, allow trial products to be purchased 2023-09-01 12:14:04 -04:00
9a88d65ff7 Merge pull request #3212 from sbwalker/dev
need to use context rather than filter
2023-08-31 14:18:31 -04:00
211d8c7f19 need to use context rather than filter 2023-08-31 14:18:19 -04:00
763cdca114 Merge pull request #3211 from sbwalker/dev
Marketplace UI consistency
2023-08-31 13:55:08 -04:00
0d56d35646 Marketplace UI consistency 2023-08-31 13:54:56 -04:00
d1e80cb86a Update README.md 2023-08-29 14:01:00 -04:00
2e116489c8 Merge pull request #3208 from oqtane/master
Merge pull request #3207 from oqtane/dev
2023-08-29 13:54:50 -04:00
a94daa1216 Merge pull request #3207 from oqtane/dev
4.0.3 Release
2023-08-29 13:54:31 -04:00
ad57d665cc Merge pull request #3205 from sbwalker/dev
filter deleted pages and modules on the server
2023-08-28 17:15:45 -04:00
db2c42f0f4 filter deleted pages and modules on the server 2023-08-28 17:15:31 -04:00
b713cf86c7 Merge pull request #3204 from sbwalker/dev
fix page template update logic
2023-08-28 16:12:47 -04:00
11d02ccf44 fix page template update logic 2023-08-28 16:12:32 -04:00
54f3c8beac Merge pull request #3203 from sbwalker/dev
documentation for ReleaseVersions property
2023-08-28 15:27:47 -04:00
2ed51bf1f3 documentation for ReleaseVersions property 2023-08-28 15:27:33 -04:00
9f859f024c Merge pull request #3202 from sbwalker/dev
improve solution related to module installation issue where ModuleDefinition Version property was not being populated correctly (#3175)
2023-08-28 15:20:29 -04:00
d47175df8a improve solution related to module installation issue where ModuleDefinition Version property was not being populated correctly (#3175) 2023-08-28 15:20:09 -04:00
d6a80d36b1 Merge pull request #3200 from sbwalker/dev
abstract namespace specification to template.json so that module and theme templates can use their own naming conventions
2023-08-28 09:03:26 -04:00
a857e3f31e abstract namespace specification to template.json so that module and theme templates can use their own naming conventions 2023-08-28 09:03:05 -04:00
918dfb05cd Merge pull request #3199 from sbwalker/dev
prepare for 4.0.3 release
2023-08-28 07:58:49 -04:00
f22e0c4384 prepare for 4.0.3 release 2023-08-28 07:58:33 -04:00
daa08626ae Merge pull request #3198 from sbwalker/dev
support ~ notation for license urls
2023-08-28 07:49:22 -04:00
45592e6acd support ~ notation for license urls 2023-08-28 07:49:06 -04:00
6944eb3392 Merge pull request #3197 from sbwalker/dev
allow module or theme License property to be a Url
2023-08-27 08:57:50 -04:00
85f20aca58 allow module or theme License property to be a Url 2023-08-27 08:57:30 -04:00
ca8d8b3587 Merge pull request #3196 from sbwalker/dev
move password requirement localization back to Client project
2023-08-27 08:43:43 -04:00
74921a0686 move password requirement localization back to Client project 2023-08-27 08:43:23 -04:00
be4296f288 Merge pull request #3195 from sbwalker/dev
include only distinct items which have package names
2023-08-25 17:44:39 -04:00
836de56276 include only distinct items which have package names 2023-08-25 17:44:27 -04:00
ef60ac3836 Merge pull request #3194 from sbwalker/dev
optimize package update queries
2023-08-25 15:42:58 -04:00
bdd1ba05e8 optimize package update queries 2023-08-25 15:42:45 -04:00
f0a22d5517 Merge pull request #3193 from sbwalker/dev
remove Module Creator in favor of using Create Module in Module Management
2023-08-25 13:49:08 -04:00
417c8d7874 remove Module Creator in favor of using Create Module in Module Management 2023-08-25 13:48:51 -04:00
5a111cdb2a Merge pull request #3192 from sbwalker/dev
fix #3174 - display accurate password complexity requirements (this is now implemented in registration, user profiles, and user management - add/edit)
2023-08-25 13:31:22 -04:00
ef2f779f71 fix #3174 - display accurate password complexity requirements (this is now implemented in registration, user profiles, and user management - add/edit) 2023-08-25 13:31:02 -04:00
f0fd0db7bb Merge pull request #3191 from sbwalker/dev
simplified method names
2023-08-25 12:30:01 -04:00
b4ab45d2e7 simplified method names 2023-08-25 12:29:46 -04:00
73c4bcee30 Merge pull request #3185 from leigh-pointer/SchedularTimeControls
Fix for Schedular Allows incorrect Time format #3184
2023-08-25 10:54:13 -04:00
d4daf098e1 Merge pull request #3190 from sbwalker/dev
resolve #3189 - make path a querystring parameter
2023-08-25 09:49:22 -04:00
95de1fff69 resolve #3189 - make path a querystring parameter 2023-08-25 09:49:07 -04:00
96480b4382 Override and 2 new functions 2023-08-25 12:09:16 +02:00
14ae553557 Merge pull request #3188 from sbwalker/dev
remove unecessary NotMapped attribute
2023-08-24 16:42:30 -04:00
4de809e275 remove unecessary NotMapped attribute 2023-08-24 16:41:58 -04:00
a383382529 Merge pull request #3187 from sbwalker/dev
include PackageRegistryUrl in System Info
2023-08-24 16:39:51 -04:00
d2b3061ed9 include PackageRegistryUrl in System Info 2023-08-24 16:39:38 -04:00
073b10929a Fix for Schedular Allows incorrect Time format #3184
Modified code to accept correct time types.
2023-08-24 18:37:33 +02:00
6a2cfcab34 Merge pull request #3183 from leigh-pointer/IconResources
Update IconRescources
2023-08-24 11:38:09 -04:00
00c85ae7d3 Update IconResources.resx 2023-08-24 17:03:24 +02:00
d1b1e88389 Update IconRescources 2023-08-24 15:46:24 +02:00
5679ff5df9 Merge pull request #3182 from sbwalker/dev
add refresh button to module and theme installation page
2023-08-24 09:33:46 -04:00
3f28b39da0 add refresh button to module and theme installation page 2023-08-24 09:33:32 -04:00
ee21536742 Merge pull request #3181 from sbwalker/dev
reverse InputList Dictionary usage
2023-08-24 08:50:45 -04:00
6fafeedeb9 reverse InputList Dictionary usage 2023-08-24 08:50:33 -04:00
d86c53858e Merge pull request #3180 from sbwalker/dev
added defensive logic to resolve regression issue caused by #3175
2023-08-24 08:10:06 -04:00
0a22f80942 added defensive logic to resolve regression issue caused by #3175 2023-08-24 08:09:53 -04:00
fb247197e8 Merge pull request #3179 from sbwalker/dev
Allow InputList component to be localizable and support multiple instances on a page. Implement icon localization in Page Add/Edit using IconResources.resx
2023-08-23 16:43:36 -04:00
261ed05fa3 Allow InputList component to be localizable and support multiple instances on a page. Implement icon localization in Page Add/Edit using IconResources.resx 2023-08-23 16:43:14 -04:00
f5ce48a7c2 Merge pull request #3178 from sbwalker/dev
move icon loading reflection logic to server
2023-08-23 15:25:54 -04:00
82d128974c move icon loading reflection logic to server 2023-08-23 15:25:39 -04:00
34ae731959 Merge pull request #3169 from leigh-pointer/InputList
Add InputList control to select values from a dictionary of string
2023-08-23 14:29:38 -04:00
030a92d048 Merge pull request #3171 from vnetonline/fix-3163
[FIX] Added the ability to clear the Message in control panel
2023-08-23 14:29:28 -04:00
a327358b7a Merge pull request #3177 from sbwalker/dev
add support for background color padding during image resizing
2023-08-23 10:05:46 -04:00
9bd078d3e9 add support for background color padding during image resizing 2023-08-23 10:05:27 -04:00
412405e22c Merge pull request #3176 from sbwalker/dev
improve UX for Extend license option
2023-08-23 09:39:04 -04:00
2b20b34a74 improve UX for Extend license option 2023-08-23 09:38:48 -04:00
f269a07463 Merge pull request #3175 from sbwalker/dev
resolve module installation issue where ModuleDefinition Version property was not being populated correctly
2023-08-23 09:24:56 -04:00
d9948e8ee0 resolve module installation issue where ModuleDefinition Version property was not being populated correctly 2023-08-23 09:24:36 -04:00
ddb2fe4b03 Update Edit.razor
Moved Icon to the end of control
2023-08-23 07:49:50 +02:00
471f7184fb Added Icon Preview
Added an icon preview to the icon selecting control
2023-08-23 07:47:35 +02:00
92ea5da358 [FIX] Added the ability to clear the Message in control panel on open or close
This fix is related to issue #3163
2023-08-23 10:13:16 +10:00
09f1d3ca15 Add InputList control to select values from a dictionary of string
The control accepts a dictionary of string that can be searched using an input control.
Pages Add and Edit have implemented the control to render the list of Icons found in the Oqtane Shared namespace.
2023-08-22 09:50:59 +02:00
3729b8eac2 Merge pull request #3161 from rcpacheco/rcp_show_passwd_dbs
Update database installers to show/hide passwd
2023-08-21 11:02:29 -04:00
ca5f345414 Update database installers to show/hide passwd 2023-08-18 13:25:32 -06:00
15cf1e8a53 Merge pull request #3160 from sbwalker/dev
fix #3157 - provide support for asynchronous scheduled jobs
2023-08-18 13:34:52 -04:00
da74b0ece1 fix #3157 - provide support for asynchronous scheduled jobs 2023-08-18 13:34:38 -04:00
f50fe6f22f Merge pull request #3158 from sbwalker/dev
add support for * ImageSizes for folders
2023-08-18 10:55:25 -04:00
542eec2a9e add support for * ImageSizes for folders 2023-08-18 10:55:10 -04:00
4c5460fc9e Merge pull request #3156 from sbwalker/dev
migrate LocalizerFactory logic from SiteRouter to ModuleTitle component
2023-08-17 08:23:30 -04:00
394b8f1ce6 migrate LocalizerFactory logic from SiteRouter to ModuleTitle component 2023-08-17 08:23:17 -04:00
abeeda1a2b Merge pull request #3155 from sbwalker/dev
rollback #3125 and localize module component Title using LocalizerFactory
2023-08-17 07:56:58 -04:00
9e6ea3f486 rollback #3125 and localize module component Title using LocalizerFactory 2023-08-17 07:56:39 -04:00
2777a0946c Merge pull request #3154 from vnetonline/enhance-expando-object
[ENHANCE] - Change to ExpandoObject instead of an Anonymous Object
2023-08-17 07:47:57 -04:00
7f43c2659a Merge pull request #3153 from leigh-pointer/UserSort
Update the TH with link-primary Decoration classes
2023-08-17 07:46:39 -04:00
a4fa11c881 [ENHANCE] - Change to ExpandoObject instead of an Anonymous Object
Anonymous Object are not able to be used across assemblies however ExpandoObject is refer to #3145
2023-08-17 10:45:38 +10:00
8230e51c05 Update the TH with link-primary Decoration classes
This emphasizes that the header is clickable.
2023-08-16 19:09:12 +02:00
6e62d4791c Update the TH with link-primary Decoration classes
This emphasizes that the header is clickable.
2023-08-16 19:06:59 +02:00
ce8abdb8cd Merge pull request #3152 from sbwalker/dev
modify column size to prevent text wrapping
2023-08-16 12:25:53 -04:00
941bb7edaa modify column size to prevent text wrapping 2023-08-16 12:25:39 -04:00
1acbdf8b9e Merge pull request #3150 from alikoli/dev
Fix missing translations part 3
2023-08-15 15:51:58 -04:00
530804c847 Merge pull request #3151 from sbwalker/dev
In FileManager fix the id handling for the progressinfo and progressbar and include OnSelect events on Upload and Delete. Add missing backend implementation for AddFileAsync
2023-08-15 15:51:32 -04:00
b00b426e9c fix the id handling for the progressinfo and progressbar, include OnSelect events on Upload and Delete, add missing backend implementation for AddFileAsync 2023-08-15 15:50:35 -04:00
128f1753c0 Remove resx backups 2023-08-15 16:17:23 +02:00
1922629d27 Merge branch 'oqtane:dev' into dev 2023-08-15 16:12:21 +02:00
283a03a7cc Fix missing translations part 3 2023-08-15 16:09:59 +02:00
cdab26a97b Merge pull request #3146 from HonesDK/dev
Fixed localization in AuditInfo
2023-08-15 08:03:29 -04:00
5d3311c46b Merge pull request #3147 from sbwalker/dev
Notification job must convert \n to <br /> now that IsNodyHtml is set to True
2023-08-14 17:03:43 -04:00
7cbc21671b Notification job must convert \n to <br /> now that IsNodyHtml is set to True 2023-08-14 17:03:21 -04:00
d14d820e25 Merge branch 'dev' of https://github.com/HonesDK/oqtane.framework into dev 2023-08-14 16:54:41 +02:00
b567d02df2 Fixed localization in AuditInfo 2023-08-14 16:52:30 +02:00
dbb58541f2 Revert "Fixed modified by localization, and added DateTimeFormat to localization"
This reverts commit 4a8bcc6f71.
2023-08-14 16:51:21 +02:00
5a42caf4f8 Merge pull request #3143 from sbwalker/dev
add ability to get user based on username or email address
2023-08-13 08:35:24 -04:00
c2acd010ce add ability to get user based on username or email address 2023-08-13 08:35:03 -04:00
4a8bcc6f71 Fixed modified by localization, and added DateTimeFormat to localization 2023-08-13 10:11:29 +02:00
89e4dc7a08 Merge pull request #3141 from sbwalker/dev
change "price" to "from" to reflect multiple options
2023-08-12 10:31:56 -04:00
c344eedb12 change "price" to "from" to reflect multiple options 2023-08-12 10:31:44 -04:00
0aeb1a62b9 Merge pull request #3137 from sbwalker/dev
fix #3134 improve parsing of headcontent to handle space delimiters
2023-08-11 15:53:43 -04:00
316e0f5a68 fix #3134 improve parsing of headcontent to handle space delimiters 2023-08-11 15:53:32 -04:00
6f9314f76f Merge pull request #3133 from alikoli/dev
Fix missing translations part 2
2023-08-11 15:33:21 -04:00
6ef21795ba fix heading 2023-08-10 02:13:31 +02:00
e44855493e fix heading 2023-08-10 02:12:36 +02:00
101a3c8af5 Revert launchSettings 2023-08-10 02:03:46 +02:00
8fbbbce4ec Fix missing translations part 2 2023-08-10 01:52:46 +02:00
1de8bb850f Update README.md 2023-08-09 08:54:45 -04:00
25d09186fe Update README.md 2023-08-09 08:34:35 -04:00
9552b7c6f9 Update README.md 2023-08-09 08:34:13 -04:00
158 changed files with 3051 additions and 1234 deletions

View File

@ -0,0 +1,14 @@
namespace Oqtane
{
/// <summary>
/// Dummy class used to collect shared resource strings for this application
/// </summary>
/// <remarks>
/// This class is mostly used with IStringLocalizer and IHtmlLocalizer interfaces.
/// The class must reside at the project root.
/// </remarks>
public class IconResources
{
}
}

View File

@ -1,5 +1,6 @@
@namespace Oqtane.Installer.Controls @namespace Oqtane.Installer.Controls
@implements Oqtane.Interfaces.IDatabaseConfigControl @implements Oqtane.Interfaces.IDatabaseConfigControl
@inject IStringLocalizer<SharedResources> SharedLocalizer
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="server" HelpText="Enter the database server" ResourceKey="Server">Server:</Label> <Label Class="col-sm-3" For="server" HelpText="Enter the database server" ResourceKey="Server">Server:</Label>
@ -28,7 +29,10 @@
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="pwd" HelpText="Enter the password to use for the database" ResourceKey="Pwd">Password:</Label> <Label Class="col-sm-3" For="pwd" HelpText="Enter the password to use for the database" ResourceKey="Pwd">Password:</Label>
<div class="col-sm-9"> <div class="col-sm-9">
<input id="pwd" type="password" class="form-control" @bind="@_pwd" autocomplete="new-password" /> <div class="input-group">
<input id="pwd" type="@_passwordType" class="form-control" @bind="@_pwd" autocomplete="new-password" />
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword" tabindex="-1">@_togglePassword</button>
</div>
</div> </div>
</div> </div>
@ -38,6 +42,13 @@
private string _database = "Oqtane-" + DateTime.UtcNow.ToString("yyyyMMddHHmm"); private string _database = "Oqtane-" + DateTime.UtcNow.ToString("yyyyMMddHHmm");
private string _uid = String.Empty; private string _uid = String.Empty;
private string _pwd = String.Empty; private string _pwd = String.Empty;
private string _passwordType = "password";
private string _togglePassword = string.Empty;
protected override void OnInitialized()
{
_togglePassword = SharedLocalizer["ShowPassword"];
}
public string GetConnectionString() public string GetConnectionString()
{ {
@ -55,4 +66,18 @@
return connectionString; return connectionString;
} }
private void TogglePassword()
{
if (_passwordType == "password")
{
_passwordType = "text";
_togglePassword = SharedLocalizer["HidePassword"];
}
else
{
_passwordType = "password";
_togglePassword = SharedLocalizer["ShowPassword"];
}
}
} }

View File

@ -1,6 +1,7 @@
@namespace Oqtane.Installer.Controls @namespace Oqtane.Installer.Controls
@implements Oqtane.Interfaces.IDatabaseConfigControl @implements Oqtane.Interfaces.IDatabaseConfigControl
@inject IStringLocalizer<PostgreSQLConfig> Localizer @inject IStringLocalizer<PostgreSQLConfig> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="server" HelpText="Enter the database server" ResourceKey="Server">Server:</Label> <Label Class="col-sm-3" For="server" HelpText="Enter the database server" ResourceKey="Server">Server:</Label>
@ -40,7 +41,10 @@
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="pwd" HelpText="Enter the password to use for the database" ResourceKey="Pwd">Password:</Label> <Label Class="col-sm-3" For="pwd" HelpText="Enter the password to use for the database" ResourceKey="Pwd">Password:</Label>
<div class="col-sm-9"> <div class="col-sm-9">
<input id="pwd" type="password" class="form-control" @bind="@_pwd" autocomplete="new-password" /> <div class="input-group">
<input id="pwd" type="@_passwordType" class="form-control" @bind="@_pwd" autocomplete="new-password" />
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword" tabindex="-1">@_togglePassword</button>
</div>
</div> </div>
</div> </div>
} }
@ -52,6 +56,13 @@
private string _security = "integrated"; private string _security = "integrated";
private string _uid = String.Empty; private string _uid = String.Empty;
private string _pwd = String.Empty; private string _pwd = String.Empty;
private string _passwordType = "password";
private string _togglePassword = string.Empty;
protected override void OnInitialized()
{
_togglePassword = SharedLocalizer["ShowPassword"];
}
public string GetConnectionString() public string GetConnectionString()
{ {
@ -80,4 +91,18 @@
return connectionString; return connectionString;
} }
private void TogglePassword()
{
if (_passwordType == "password")
{
_passwordType = "text";
_togglePassword = SharedLocalizer["HidePassword"];
}
else
{
_passwordType = "password";
_togglePassword = SharedLocalizer["ShowPassword"];
}
}
} }

View File

@ -35,7 +35,10 @@
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="pwd" HelpText="Enter the password to use for the database" ResourceKey="Pwd">Password:</Label> <Label Class="col-sm-3" For="pwd" HelpText="Enter the password to use for the database" ResourceKey="Pwd">Password:</Label>
<div class="col-sm-9"> <div class="col-sm-9">
<input id="pwd" type="password" class="form-control" @bind="@_pwd" autocomplete="new-password" /> <div class="input-group">
<input id="pwd" type="@_passwordType" class="form-control" @bind="@_pwd" autocomplete="new-password" />
<button type="button" class="btn btn-secondary" @onclick="@TogglePassword" tabindex="-1">@_togglePassword</button>
</div>
</div> </div>
</div> </div>
} }
@ -67,9 +70,16 @@
private string _security = "integrated"; private string _security = "integrated";
private string _uid = String.Empty; private string _uid = String.Empty;
private string _pwd = String.Empty; private string _pwd = String.Empty;
private string _passwordType = "password";
private string _togglePassword = string.Empty;
private string _encryption = "false"; private string _encryption = "false";
private string _trustservercertificate = "false"; private string _trustservercertificate = "false";
protected override void OnInitialized()
{
_togglePassword = SharedLocalizer["ShowPassword"];
}
public string GetConnectionString() public string GetConnectionString()
{ {
var connectionString = String.Empty; var connectionString = String.Empty;
@ -92,4 +102,18 @@
return connectionString; return connectionString;
} }
private void TogglePassword()
{
if (_passwordType == "password")
{
_passwordType = "text";
_togglePassword = SharedLocalizer["HidePassword"];
}
else
{
_passwordType = "password";
_togglePassword = SharedLocalizer["ShowPassword"];
}
}
} }

View File

@ -66,12 +66,6 @@
public override string Title => "File Management"; public override string Title => "File Management";
protected override void OnParametersSet()
{
base.OnParametersSet();
base.SetModuleTitle(Localizer["ModuleTitle.Text"]);
}
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
try try

View File

@ -48,7 +48,7 @@
</div> </div>
</div> </div>
<div class="row mb-1 align-items-center"> <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,x200,200x)" ResourceKey="ImageSizes">Image Sizes: </Label> <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"> <div class="col-sm-9">
<input id="imagesizes" class="form-control" @bind="@_imagesizes" maxlength="512" /> <input id="imagesizes" class="form-control" @bind="@_imagesizes" maxlength="512" />
</div> </div>
@ -112,11 +112,6 @@
public override string Title => "Folder Management"; public override string Title => "Folder Management";
protected override void OnParametersSet()
{
base.OnParametersSet();
base.SetModuleTitle(Localizer["ModuleTitle.Text"]);
}
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
try try

View File

@ -56,7 +56,7 @@
<input id="starting" type="date" class="form-control" @bind="@_startDate" /> <input id="starting" type="date" class="form-control" @bind="@_startDate" />
</div> </div>
<div class="col"> <div class="col">
<input id="starting" type="text" class="form-control" placeholder="hh:mm" @bind="@_startTime" /> <input id="starting" type="time" class="form-control" placeholder="hh:mm" @bind="@_startTime" />
</div> </div>
</div> </div>
</div> </div>
@ -69,7 +69,7 @@
<input id="ending" type="date" class="form-control" @bind="@_endDate" /> <input id="ending" type="date" class="form-control" @bind="@_endDate" />
</div> </div>
<div class="col"> <div class="col">
<input id="ending" type="text" class="form-control" placeholder="hh:mm" @bind="@_endTime" /> <input id="ending" type="time" class="form-control" placeholder="hh:mm" @bind="@_endTime" />
</div> </div>
</div> </div>
</div> </div>
@ -82,7 +82,7 @@
<input id="next" type="date" class="form-control" @bind="@_nextDate" /> <input id="next" type="date" class="form-control" @bind="@_nextDate" />
</div> </div>
<div class="col"> <div class="col">
<input id="next" type="text" class="form-control" placeholder="hh:mm" @bind="@_nextTime" /> <input id="next" type="time" class="form-control" placeholder="hh:mm" @bind="@_nextTime" />
</div> </div>
</div> </div>
</div> </div>
@ -106,12 +106,12 @@
private string _interval = string.Empty; private string _interval = string.Empty;
private string _frequency = string.Empty; private string _frequency = string.Empty;
private DateTime? _startDate = null; private DateTime? _startDate = null;
private string _startTime = string.Empty; private DateTime? _startTime = null;
private DateTime? _endDate = null; private DateTime? _endDate = null;
private string _endTime = string.Empty; private DateTime? _endTime = null;
private string _retentionHistory = string.Empty; private string _retentionHistory = string.Empty;
private DateTime? _nextDate = null; private DateTime? _nextDate = null;
private string _nextTime = string.Empty; private DateTime? _nextTime = null;
private string createdby; private string createdby;
private DateTime createdon; private DateTime createdon;
private string modifiedby; private string modifiedby;
@ -132,10 +132,13 @@
_isEnabled = job.IsEnabled.ToString(); _isEnabled = job.IsEnabled.ToString();
_interval = job.Interval.ToString(); _interval = job.Interval.ToString();
_frequency = job.Frequency; _frequency = job.Frequency;
(_startDate, _startTime) = Utilities.UtcAsLocalDateAndTime(job.StartDate); _startDate = Utilities.UtcAsLocalDate(job.StartDate);
(_endDate, _endTime) = Utilities.UtcAsLocalDateAndTime(job.EndDate); _startTime = Utilities.UtcAsLocalDateTime(job.StartDate);
_endDate = Utilities.UtcAsLocalDate(job.EndDate);
_endTime = Utilities.UtcAsLocalDateTime(job.EndDate);
_retentionHistory = job.RetentionHistory.ToString(); _retentionHistory = job.RetentionHistory.ToString();
(_nextDate, _nextTime) = Utilities.UtcAsLocalDateAndTime(job.NextExecution); _nextDate = Utilities.UtcAsLocalDate(job.NextExecution);
_nextTime = Utilities.UtcAsLocalDateTime(job.NextExecution);
createdby = job.CreatedBy; createdby = job.CreatedBy;
createdon = job.CreatedOn; createdon = job.CreatedOn;
modifiedby = job.ModifiedBy; modifiedby = job.ModifiedBy;

View File

@ -146,7 +146,7 @@ else
{ {
try try
{ {
_package = await PackageService.GetPackageAsync(Constants.PackageId + "." + code, version); _package = await PackageService.GetPackageAsync(Constants.PackageId + "." + code, version, false);
if (_package != null) if (_package != null)
{ {
StateHasChanged(); StateHasChanged();
@ -168,7 +168,7 @@ else
{ {
try try
{ {
await PackageService.DownloadPackageAsync(_package.PackageId, _package.Version, Constants.PackagesFolder); await PackageService.DownloadPackageAsync(_package.PackageId, _package.Version);
await logger.LogInformation("Language Package {Name} {Version} Downloaded Successfully", _package.PackageId, _package.Version); await logger.LogInformation("Language Package {Name} {Version} Downloaded Successfully", _package.PackageId, _package.Version);
AddModuleMessage(string.Format(Localizer["Success.Language.Download"], NavigateUrl("admin/system")), MessageType.Success); AddModuleMessage(string.Format(Localizer["Success.Language.Download"], NavigateUrl("admin/system")), MessageType.Success);
_package = null; _package = null;

View File

@ -3,6 +3,7 @@
@inherits ModuleBase @inherits ModuleBase
@inject NavigationManager NavigationManager @inject NavigationManager NavigationManager
@inject IUserService UserService @inject IUserService UserService
@inject ISettingService SettingService
@inject IServiceProvider ServiceProvider @inject IServiceProvider ServiceProvider
@inject IStringLocalizer<Index> Localizer @inject IStringLocalizer<Index> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer @inject IStringLocalizer<SharedResources> SharedLocalizer
@ -92,23 +93,23 @@
{ {
try try
{ {
_togglepassword = SharedLocalizer["ShowPassword"];
if (PageState.Site.Settings.ContainsKey("LoginOptions:AllowSiteLogin") && !string.IsNullOrEmpty(PageState.Site.Settings["LoginOptions:AllowSiteLogin"]))
{
_allowsitelogin = bool.Parse(PageState.Site.Settings["LoginOptions:AllowSiteLogin"]);
}
if (PageState.Site.Settings.ContainsKey("ExternalLogin:ProviderType") && !string.IsNullOrEmpty(PageState.Site.Settings["ExternalLogin:ProviderType"]))
{
_allowexternallogin = true;
}
if (PageState.QueryString.ContainsKey("returnurl")) if (PageState.QueryString.ContainsKey("returnurl"))
{ {
_returnUrl = PageState.QueryString["returnurl"]; _returnUrl = PageState.QueryString["returnurl"];
} }
_allowexternallogin = (SettingService.GetSetting(PageState.Site.Settings, "ExternalLogin:ProviderType", "") != "") ? true : false;
_allowsitelogin = bool.Parse(SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:AllowSiteLogin", "true"));
if (_allowexternallogin && !_allowsitelogin)
{
// redirect to external login
NavigationManager.NavigateTo(Utilities.TenantUrl(PageState.Alias, "/pages/external?returnurl=" + _returnUrl), true);
return;
}
_togglepassword = SharedLocalizer["ShowPassword"];
if (PageState.QueryString.ContainsKey("name")) if (PageState.QueryString.ContainsKey("name"))
{ {
_username = PageState.QueryString["name"]; _username = PageState.QueryString["name"];
@ -167,7 +168,7 @@
protected override async Task OnAfterRenderAsync(bool firstRender) protected override async Task OnAfterRenderAsync(bool firstRender)
{ {
if (firstRender && PageState.User == null) if (firstRender && PageState.User == null && _allowsitelogin)
{ {
await username.FocusAsync(); await username.FocusAsync();
} }
@ -206,8 +207,7 @@
if (hybrid) if (hybrid)
{ {
// hybrid apps utilize an interactive login // hybrid apps utilize an interactive login
var authstateprovider = (IdentityAuthenticationStateProvider)ServiceProvider var authstateprovider = (IdentityAuthenticationStateProvider)ServiceProvider.GetService(typeof(IdentityAuthenticationStateProvider));
.GetService(typeof(IdentityAuthenticationStateProvider));
authstateprovider.NotifyAuthenticationChanged(); authstateprovider.NotifyAuthenticationChanged();
NavigationManager.NavigateTo(NavigateUrl(WebUtility.UrlDecode(_returnUrl), true)); NavigationManager.NavigateTo(NavigateUrl(WebUtility.UrlDecode(_returnUrl), true));
} }
@ -221,7 +221,7 @@
} }
else else
{ {
if ((PageState.Site.Settings.ContainsKey("LoginOptions:TwoFactor") && PageState.Site.Settings["LoginOptions:TwoFactor"] == "required") || user.TwoFactorRequired) if (SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:TwoFactor", "false") == "required" || user.TwoFactorRequired)
{ {
twofactor = true; twofactor = true;
validated = false; validated = false;

View File

@ -1,203 +0,0 @@
@namespace Oqtane.Modules.Admin.ModuleCreator
@inherits ModuleBase
@using System.Text.RegularExpressions
@inject NavigationManager NavigationManager
@inject IModuleDefinitionService ModuleDefinitionService
@inject IModuleService ModuleService
@inject ISettingService SettingService
@inject IStringLocalizer<Index> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer
@if (string.IsNullOrEmpty(_moduledefinitionname) && _templates != 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="owner" HelpText="Enter the name of the organization who is developing this module. It should not contain spaces or punctuation." ResourceKey="OwnerName">Owner Name: </Label>
<div class="col-sm-9">
<input id="owner" class="form-control" @bind="@_owner" required />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="module" HelpText="Enter a name for this module. It should not contain spaces or punctuation." ResourceKey="ModuleName">Module Name: </Label>
<div class="col-sm-9">
<input id="module" class="form-control" @bind="@_module" required />
</div>
</div>
<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" ></textarea>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="template" HelpText="Select a module template. Templates are located in the wwwroot/Modules/Templates folder on the server." ResourceKey="Template">Template: </Label>
<div class="col-sm-9">
<select id="template" class="form-select" @onchange="(e => TemplateChanged(e))" required>
<option value="-">&lt;@Localizer["Template.Select"]&gt;</option>
@foreach (Template template in _templates)
{
<option value="@template.Name">@template.Title</option>
}
</select>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="reference" HelpText="Select a framework reference version" ResourceKey="FrameworkReference">Framework Reference: </Label>
<div class="col-sm-9">
<select id="reference" class="form-select" @bind="@_reference" required>
@foreach (string version in _versions)
{
if (Version.Parse(version).CompareTo(Version.Parse(_minversion)) >= 0)
{
<option value="@(version)">@(version)</option>
}
}
<option value="local">@SharedLocalizer["LocalVersion"]</option>
</select>
</div>
</div>
@if (!string.IsNullOrEmpty(_location))
{
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="location" HelpText="Location where the module will be created" ResourceKey="Location">Location: </Label>
<div class="col-sm-9">
<input id="module" class="form-control" @bind="@_location" readonly />
</div>
</div>
}
</div>
</form>
<button type="button" class="btn btn-success" @onclick="CreateModule">@Localizer["Module.Create"]</button>
}
else
{
<button type="button" class="btn btn-success" @onclick="ActivateModule">@Localizer["Module.Activate"]</button>
}
@code {
private ElementReference form;
private bool validated = false;
private string _moduledefinitionname = string.Empty;
private string _owner = string.Empty;
private string _module = string.Empty;
private string _description = string.Empty;
private List<Template> _templates;
private string _template = "-";
private string[] _versions;
private string _reference = "local";
private string _minversion = "2.0.0";
private string _location = string.Empty;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
protected override void OnInitialized()
{
_moduledefinitionname = SettingService.GetSetting(ModuleState.Settings, "ModuleDefinitionName", "");
if (string.IsNullOrEmpty(_moduledefinitionname))
{
AddModuleMessage(Localizer["Info.Module.Creator"], MessageType.Info);
}
else
{
AddModuleMessage(Localizer["Info.Module.Activate"], MessageType.Info);
}
}
protected override async Task OnParametersSetAsync()
{
try
{
_templates = await ModuleDefinitionService.GetModuleDefinitionTemplatesAsync();
_versions = Constants.ReleaseVersions.Split(',').Where(item => Version.Parse(item).CompareTo(Version.Parse("2.0.0")) >= 0).ToArray();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading Module Creator");
}
}
private async Task CreateModule()
{
validated = true;
var interop = new Interop(JSRuntime);
if (await interop.FormValid(form))
{
try
{
if (IsValid(_owner) && IsValid(_module) && _owner != _module && _template != "-")
{
var moduleDefinition = new ModuleDefinition { Owner = _owner, Name = _module, Description = _description, Template = _template, Version = _reference };
moduleDefinition = await ModuleDefinitionService.CreateModuleDefinitionAsync(moduleDefinition);
var settings = await SettingService.GetModuleSettingsAsync(ModuleState.ModuleId);
settings = SettingService.SetSetting(settings, "ModuleDefinitionName", moduleDefinition.ModuleDefinitionName);
await SettingService.UpdateModuleSettingsAsync(settings, ModuleState.ModuleId);
GetLocation();
AddModuleMessage(string.Format(Localizer["Success.Module.Create"], NavigateUrl("admin/system")), MessageType.Success);
}
else
{
AddModuleMessage(Localizer["Message.Require.ValidName"], MessageType.Warning);
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Creating Module");
}
}
else
{
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning);
}
}
private async Task ActivateModule()
{
try
{
if (!string.IsNullOrEmpty(_moduledefinitionname))
{
Module module = await ModuleService.GetModuleAsync(ModuleState.ModuleId);
module.ModuleDefinitionName = _moduledefinitionname;
await ModuleService.UpdateModuleAsync(module);
NavigationManager.NavigateTo(NavigateUrl(), true);
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Activating Module");
}
}
private bool IsValid(string name)
{
// must contain letters, underscores and digits and first character must be letter or underscore
return !string.IsNullOrEmpty(name) && name.ToLower() != "module" && !name.ToLower().Contains("oqtane") && Regex.IsMatch(name, "^[A-Za-z_][A-Za-z0-9_]*$");
}
private void TemplateChanged(ChangeEventArgs e)
{
_template = (string)e.Value;
_minversion = "2.0.0";
if (_template != "-")
{
var template = _templates.FirstOrDefault(item => item.Name == _template);
_minversion = template.Version;
}
GetLocation();
}
private void GetLocation()
{
_location = string.Empty;
if (_owner != "" && _module != "" && _template != "-")
{
var template = _templates.FirstOrDefault(item => item.Name == _template);
_location = template.Location + _owner + ".Module." + _module;
}
StateHasChanged();
}
}

View File

@ -1,17 +0,0 @@
using Oqtane.Documentation;
using Oqtane.Models;
namespace Oqtane.Modules.Admin.ModuleCreator
{
[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 = "Module Creator",
Description = "Enables software developers to quickly create modules by automating many of the initial module creation tasks",
Version = "1.0.0",
Categories = "Developer"
};
}
}

View File

@ -8,7 +8,7 @@
@inject IStringLocalizer<SharedResources> SharedLocalizer @inject IStringLocalizer<SharedResources> SharedLocalizer
<TabStrip> <TabStrip>
<TabPanel Name="Download" ResourceKey="Download"> <TabPanel Name="Download" ResourceKey="Download" Heading="Download">
<div class="row justify-content-center mb-3"> <div class="row justify-content-center mb-3">
<div class="text-center"> <div class="text-center">
<div class="form-check form-check-inline"> <div class="form-check form-check-inline">
@ -28,6 +28,7 @@
<input id="search" class="form-control" placeholder="@SharedLocalizer["Search.Hint"]" @bind="@_search" /> <input id="search" class="form-control" placeholder="@SharedLocalizer["Search.Hint"]" @bind="@_search" />
<button type="button" class="btn btn-primary" @onclick="Search">@SharedLocalizer["Search"]</button> <button type="button" class="btn btn-primary" @onclick="Search">@SharedLocalizer["Search"]</button>
<button type="button" class="btn btn-secondary" @onclick="Reset">@SharedLocalizer["Reset"]</button> <button type="button" class="btn btn-secondary" @onclick="Reset">@SharedLocalizer["Reset"]</button>
<button type="button" class="btn btn-primary ms-2" @onclick="Refresh"><span class="@Icons.Reload" aria-hidden="true"></span></button>
</div> </div>
</div> </div>
</div> </div>
@ -47,7 +48,10 @@
<select class="form-select" value="@_sort" @onchange="(e => SortChanged(e))"> <select class="form-select" value="@_sort" @onchange="(e => SortChanged(e))">
<option value="popularity">@SharedLocalizer["Search.Popularity"]</option> <option value="popularity">@SharedLocalizer["Search.Popularity"]</option>
<option value="alphabetical">@SharedLocalizer["Search.Alphabetical"]</option> <option value="alphabetical">@SharedLocalizer["Search.Alphabetical"]</option>
@if (_price == "free")
{
<option value="downloads">@SharedLocalizer["Search.Downloads"]</option> <option value="downloads">@SharedLocalizer["Search.Downloads"]</option>
}
<option value="recent">@SharedLocalizer["Search.RecentlyReleased"]</option> <option value="recent">@SharedLocalizer["Search.RecentlyReleased"]</option>
@if (_price == "paid") @if (_price == "paid")
{ {
@ -56,29 +60,37 @@
</select> </select>
</div> </div>
</div> </div>
<Pager Format="Grid" Items="@_packages" DisplayPages="1" PageSize="9" Toolbar="Both" Class="container-fluid px-0" RowClass="row g-0" ColumnClass="col-lg-4 col-md-6"> <Pager Format="Grid" Items="@_packages" DisplayPages="1" PageSize="9" Toolbar="Both" Class="container-fluid px-0" RowClass="row g-0" ColumnClass="col-lg-4 col-md-6" CurrentPage="@_page.ToString()" OnPageChange="OnPageChange">
<Row> <Row>
<div class="m-2 p-2 d-flex justify-content-center"> <div class="m-2 p-2 d-flex justify-content-center">
<div class="container-fluid px-0"> <div class="container-fluid px-0">
<div class="row g-0"> <div class="row g-0 mb-2">
<div class="col-6"> <div class="col-4">
@if (context.LogoFileId != null) @if (context.LogoUrl != null)
{ {
<img src="@GetLogo(context.LogoFileId.Value)" class="img-fluid" alt="@context.Name" /> <img src="@context.LogoUrl" class="img-fluid" alt="@context.Name" />
} }
else else
{ {
<img src="/package.png" class="img-fluid" alt="@context.Name" /> <img src="/package.png" class="img-fluid" alt="@context.Name" />
} }
</div> </div>
<div class="col-6 text-end"> <div class="col-8 text-end">
<small>@SharedLocalizer["Search.Version"]:</small> <strong>@context.Version</strong> <small>@SharedLocalizer["Search.Version"]:</small> <strong>@context.Version</strong>
<br /><small>@SharedLocalizer["Search.Downloads"]:</small> <strong>@(String.Format("{0:n0}", context.Downloads))</strong>
<br /><small>@SharedLocalizer["Search.Released"]:</small> <strong>@context.ReleaseDate.ToString("MM/dd/yyyy")</strong> <br /><small>@SharedLocalizer["Search.Released"]:</small> <strong>@context.ReleaseDate.ToString("MM/dd/yyyy")</strong>
@if (!string.IsNullOrEmpty(context.PackageUrl)) @if (!string.IsNullOrEmpty(context.PackageUrl))
{ {
<br /><small>@SharedLocalizer["Search.Source"]:</small> <strong>@(new Uri(context.PackageUrl).Host)</strong> <br /><small>@SharedLocalizer["Search.Source"]:</small> <strong>@(new Uri(context.PackageUrl).Host)</strong>
} }
@if (context.Price == null)
{
<br /><small>@SharedLocalizer["Search.Downloads"]:</small> <strong>@(String.Format("{0:n0}", context.Downloads))</strong>
}
else
{
<br /><small>@SharedLocalizer["From"]:</small> <strong>@context.Price.Value.ToString("$#,##0.00")</strong>
@((MarkupString)(context.TrialPeriod > 0 ? " <strong>(" + context.TrialPeriod + " Day Trial)</strong>" : ""))
}
</div> </div>
</div> </div>
<div class="row g-0"> <div class="row g-0">
@ -86,11 +98,6 @@
<h3 style="display: inline;"><a href="@context.ProductUrl" target="_blank">@context.Name</a></h3><br /> <h3 style="display: inline;"><a href="@context.ProductUrl" target="_blank">@context.Name</a></h3><br />
<small>@SharedLocalizer["Search.By"]:</small> <strong><a href="@context.OwnerUrl" target="new">@context.Owner</a></strong><br /> <small>@SharedLocalizer["Search.By"]:</small> <strong><a href="@context.OwnerUrl" target="new">@context.Owner</a></strong><br />
@(context.Description.Length > 400 ? (context.Description.Substring(0, 400) + "...") : context.Description)<br /> @(context.Description.Length > 400 ? (context.Description.Substring(0, 400) + "...") : context.Description)<br />
@if (context.Price != null && !string.IsNullOrEmpty(context.PaymentUrl))
{
<small>@SharedLocalizer["Search.Price"]:</small> <strong>@context.Price.Value.ToString("$#,##0.00")</strong>
@((MarkupString)(context.TrialPeriod > 0 ? " <strong>(" + context.TrialPeriod + " Day Trial)</strong>" : ""))
}
<br /> <br />
@if (!string.IsNullOrEmpty(context.PackageUrl)) @if (!string.IsNullOrEmpty(context.PackageUrl))
{ {
@ -162,6 +169,7 @@
@code { @code {
private bool _initialized = false; private bool _initialized = false;
private int _page = 1;
private List<Package> _packages; private List<Package> _packages;
private string _price = "free"; private string _price = "free";
private string _sort = "popularity"; private string _sort = "popularity";
@ -208,43 +216,34 @@
HideProgressIndicator(); HideProgressIndicator();
} }
private string GetLogo(int fileid)
{
var url = ImageUrl(fileid, 100, 100);
url = (!string.IsNullOrEmpty(PageState.Alias.Path)) ? url.Substring(PageState.Alias.Path.Length + 1) : url;
return Constants.PackageRegistryUrl + url;
}
private async void PriceChanged(string price) private async void PriceChanged(string price)
{ {
_price = price; _price = price;
_sort = "popularity";
await LoadModuleDefinitions(); await LoadModuleDefinitions();
StateHasChanged(); StateHasChanged();
} }
private async Task Search() private async Task Search()
{
try
{ {
await LoadModuleDefinitions(); await LoadModuleDefinitions();
} }
catch (Exception ex)
{
await logger.LogError(ex, "Error On Search");
}
}
private async Task Reset() private async Task Reset()
{ {
try _page = 1;
{
_search = ""; _search = "";
await LoadModuleDefinitions(); await LoadModuleDefinitions();
} }
catch (Exception ex)
private async Task Refresh()
{ {
await logger.LogError(ex, "Error On Reset"); await LoadModuleDefinitions();
} }
private void OnPageChange(int page)
{
_page = page;
} }
private async void SortChanged(ChangeEventArgs e) private async void SortChanged(ChangeEventArgs e)
@ -264,7 +263,7 @@
{ {
try try
{ {
var package = await PackageService.GetPackageAsync(packageid, version); var package = await PackageService.GetPackageAsync(packageid, version, false);
if (package != null) if (package != null)
{ {
_productname = package.Name; _productname = package.Name;
@ -293,7 +292,7 @@
{ {
try try
{ {
await PackageService.DownloadPackageAsync(_packageid, _packageversion, Constants.PackagesFolder); await PackageService.DownloadPackageAsync(_packageid, _packageversion);
await logger.LogInformation("Package {PackageId} {Version} Downloaded Successfully", _packageid, _packageversion); await logger.LogInformation("Package {PackageId} {Version} Downloaded Successfully", _packageid, _packageversion);
AddModuleMessage(string.Format(Localizer["Success.Module.Download"], NavigateUrl("admin/system")), MessageType.Success); AddModuleMessage(string.Format(Localizer["Success.Module.Download"], NavigateUrl("admin/system")), MessageType.Success);
_productname = ""; _productname = "";

View File

@ -88,9 +88,12 @@
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
protected override void OnInitialized() protected override void OnInitialized()
{
if (!NavigationManager.BaseUri.Contains("localhost:"))
{ {
AddModuleMessage(Localizer["Info.Module.Development"], MessageType.Info); AddModuleMessage(Localizer["Info.Module.Development"], MessageType.Info);
} }
}
protected override async Task OnParametersSetAsync() protected override async Task OnParametersSetAsync()
{ {
@ -115,12 +118,20 @@
{ {
if (IsValid(_owner) && IsValid(_module) && _owner != _module && _template != "-") if (IsValid(_owner) && IsValid(_module) && _owner != _module && _template != "-")
{ {
var moduleDefinition = new ModuleDefinition { Owner = _owner, Name = _module, Description = _description, Template = _template, Version = _reference }; if (IsValidXML(_description))
{
var template = _templates.FirstOrDefault(item => item.Name == _template);
var moduleDefinition = new ModuleDefinition { Owner = _owner, Name = _module, Description = _description, Template = _template, Version = _reference, ModuleDefinitionName = template.Namespace };
moduleDefinition = await ModuleDefinitionService.CreateModuleDefinitionAsync(moduleDefinition); moduleDefinition = await ModuleDefinitionService.CreateModuleDefinitionAsync(moduleDefinition);
GetLocation(); GetLocation();
AddModuleMessage(string.Format(Localizer["Success.Module.Create"], NavigateUrl("admin/system")), MessageType.Success); AddModuleMessage(string.Format(Localizer["Success.Module.Create"], NavigateUrl("admin/system")), MessageType.Success);
} }
else else
{
AddModuleMessage(Localizer["Message.Require.ValidDescription"], MessageType.Warning);
}
}
else
{ {
AddModuleMessage(Localizer["Message.Require.ValidName"], MessageType.Warning); AddModuleMessage(Localizer["Message.Require.ValidName"], MessageType.Warning);
} }
@ -142,6 +153,12 @@
return !string.IsNullOrEmpty(name) && name.ToLower() != "module" && !name.ToLower().Contains("oqtane") && Regex.IsMatch(name, "^[A-Za-z_][A-Za-z0-9_]*$"); return !string.IsNullOrEmpty(name) && name.ToLower() != "module" && !name.ToLower().Contains("oqtane") && Regex.IsMatch(name, "^[A-Za-z_][A-Za-z0-9_]*$");
} }
private bool IsValidXML(string description)
{
// must contain letters, digits, or spaces
return Regex.IsMatch(description, "^[A-Za-z0-9 ]+$");
}
private void TemplateChanged(ChangeEventArgs e) private void TemplateChanged(ChangeEventArgs e)
{ {
_template = (string)e.Value; _template = (string)e.Value;
@ -160,8 +177,14 @@
if (_owner != "" && _module != "" && _template != "-") if (_owner != "" && _module != "" && _template != "-")
{ {
var template = _templates.FirstOrDefault(item => item.Name == _template); var template = _templates.FirstOrDefault(item => item.Name == _template);
_location = template.Location + _owner + "." + _module; if (!string.IsNullOrEmpty(template.Namespace))
{
_location = template.Location + template.Namespace.Replace("[Owner]", _owner).Replace("[Module]", _module);
}
else
{
_location = template.Location + _owner + ".Module." + _module;
}
} }
StateHasChanged(); StateHasChanged();
} }

View File

@ -12,7 +12,7 @@
@if (_initialized) @if (_initialized)
{ {
<TabStrip> <TabStrip>
<TabPanel Name="Definition" ResourceKey="Definition"> <TabPanel Name="Definition" ResourceKey="Definition" Heading="Definition">
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate> <form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
<div class="container"> <div class="container">
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
@ -59,9 +59,26 @@
</div> </div>
</div> </div>
<div class="row mb-1 align-items-center"> <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" ResourceKey="PackageName">Package Name: </Label> <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"> <div class="col-sm-9">
@if (!string.IsNullOrEmpty(_packagename))
{
<div class="input-group">
<input id="packagename" class="form-control" @bind="@_packagename" disabled /> <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 />
}
</div> </div>
</div> </div>
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
@ -85,13 +102,14 @@
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="license" HelpText="The module license terms" ResourceKey="License">License: </Label> <Label Class="col-sm-3" For="license" HelpText="The module license terms" ResourceKey="License">License: </Label>
<div class="col-sm-9"> <div class="col-sm-9">
@if (_license.StartsWith("http") || _license.StartsWith("/") || _license.StartsWith("~"))
{
<a href="@_license.Replace("~", PageState?.Alias.BaseUrl + "/Modules/" + Utilities.GetTypeName(_moduledefinitionname))" class="btn btn-info" style="text-decoration: none !important" target="_new">@Localizer["View License"]</a>
}
else
{
<textarea id="license" class="form-control" @bind="@_license" rows="5" disabled></textarea> <textarea id="license" class="form-control" @bind="@_license" rows="5" disabled></textarea>
</div> }
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="runtimes" HelpText="The Blazor runtimes which this module supports" ResourceKey="Runtimes">Runtimes: </Label>
<div class="col-sm-9">
<input id="runtimes" class="form-control" @bind="@_runtimes" disabled />
</div> </div>
</div> </div>
</div> </div>
@ -103,7 +121,7 @@
<br /> <br />
<AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon"></AuditInfo> <AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon"></AuditInfo>
</TabPanel> </TabPanel>
<TabPanel Name="Permissions" ResourceKey="Permissions"> <TabPanel Name="Permissions" ResourceKey="Permissions" Heading="Permissions">
<div class="container"> <div class="container">
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<PermissionGrid EntityName="@EntityNames.ModuleDefinition" PermissionNames="@PermissionNames.Utilize" PermissionList="@_permissions" @ref="_permissionGrid" /> <PermissionGrid EntityName="@EntityNames.ModuleDefinition" PermissionNames="@PermissionNames.Utilize" PermissionList="@_permissions" @ref="_permissionGrid" />
@ -113,7 +131,7 @@
<button type="button" class="btn btn-success" @onclick="SaveModuleDefinition">@SharedLocalizer["Save"]</button> <button type="button" class="btn btn-success" @onclick="SaveModuleDefinition">@SharedLocalizer["Save"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink> <NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
</TabPanel> </TabPanel>
<TabPanel Name="Translations" ResourceKey="Translations"> <TabPanel Name="Translations" ResourceKey="Translations" Heading="Translations">
@if (_languages != null && _languages.Count > 0) @if (_languages != null && _languages.Count > 0)
{ {
<Pager Items="@_languages"> <Pager Items="@_languages">
@ -190,7 +208,7 @@
</p> </p>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-success" @onclick="DownloadPackage">@SharedLocalizer["Accept"]</button> <button type="button" class="btn btn-success" @onclick="DownloadTranslation">@SharedLocalizer["Accept"]</button>
<button type="button" class="btn btn-secondary" @onclick="HideModal">@SharedLocalizer["Cancel"]</button> <button type="button" class="btn btn-secondary" @onclick="HideModal">@SharedLocalizer["Cancel"]</button>
</div> </div>
</div> </div>
@ -212,11 +230,11 @@
private string _moduledefinitionname = ""; private string _moduledefinitionname = "";
private string _version; private string _version;
private string _packagename = ""; private string _packagename = "";
private string _packageurl = "";
private string _owner = ""; private string _owner = "";
private string _url = ""; private string _url = "";
private string _contact = ""; private string _contact = "";
private string _license = ""; private string _license = "";
private string _runtimes = "";
private List<Permission> _permissions = null; private List<Permission> _permissions = null;
private string _createdby; private string _createdby;
private DateTime _createdon; private DateTime _createdon;
@ -252,7 +270,6 @@
_url = moduleDefinition.Url; _url = moduleDefinition.Url;
_contact = moduleDefinition.Contact; _contact = moduleDefinition.Contact;
_license = moduleDefinition.License; _license = moduleDefinition.License;
_runtimes = moduleDefinition.Runtimes;
_permissions = moduleDefinition.PermissionList; _permissions = moduleDefinition.PermissionList;
_createdby = moduleDefinition.CreatedBy; _createdby = moduleDefinition.CreatedBy;
_createdon = moduleDefinition.CreatedOn; _createdon = moduleDefinition.CreatedOn;
@ -365,7 +382,7 @@
var version = _packages.Where(item => item.PackageId == packagename).FirstOrDefault().Version; var version = _packages.Where(item => item.PackageId == packagename).FirstOrDefault().Version;
try try
{ {
_package = await PackageService.GetPackageAsync(packagename, version); _package = await PackageService.GetPackageAsync(packagename, version, false);
if (_package != null) if (_package != null)
{ {
StateHasChanged(); StateHasChanged();
@ -383,11 +400,11 @@
} }
} }
private async Task DownloadPackage() private async Task DownloadTranslation()
{ {
try try
{ {
await PackageService.DownloadPackageAsync(_package.PackageId, _package.Version, Constants.PackagesFolder); await PackageService.DownloadPackageAsync(_package.PackageId, _package.Version);
await logger.LogInformation("Package {PackageId} {Version} Downloaded Successfully", _package.PackageId, _package.Version); await logger.LogInformation("Package {PackageId} {Version} Downloaded Successfully", _package.PackageId, _package.Version);
AddModuleMessage(string.Format(Localizer["Success.Translation.Download"], NavigateUrl("admin/system")), MessageType.Success); AddModuleMessage(string.Format(Localizer["Success.Translation.Download"], NavigateUrl("admin/system")), MessageType.Success);
_package = null; _package = null;
@ -399,4 +416,28 @@
AddModuleMessage(Localizer["Error.Translation.Download"], MessageType.Error); AddModuleMessage(Localizer["Error.Translation.Download"], MessageType.Error);
} }
} }
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

@ -37,7 +37,7 @@ else
</div> </div>
</div> </div>
<Pager Items="@_moduleDefinitions.Where(item => item.Categories.Contains(_category))"> <Pager Items="@_moduleDefinitions">
<Header> <Header>
<th style="width: 1px;">&nbsp;</th> <th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th> <th style="width: 1px;">&nbsp;</th>
@ -99,6 +99,7 @@ else
} }
@code { @code {
private List<ModuleDefinition> _allModuleDefinitions;
private List<ModuleDefinition> _moduleDefinitions; private List<ModuleDefinition> _moduleDefinitions;
private List<Package> _packages; private List<Package> _packages;
private List<string> _categories = new List<string>(); private List<string> _categories = new List<string>();
@ -110,9 +111,9 @@ else
{ {
try try
{ {
_moduleDefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync(PageState.Site.SiteId); _allModuleDefinitions = await ModuleDefinitionService.GetModuleDefinitionsAsync(PageState.Site.SiteId);
_packages = await PackageService.GetPackagesAsync("module"); _categories = _allModuleDefinitions.SelectMany(m => m.Categories.Split(',')).Distinct().ToList();
_categories = _moduleDefinitions.SelectMany(m => m.Categories.Split(',')).Distinct().ToList(); await LoadModuleDefinitions();
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -124,6 +125,12 @@ else
} }
} }
private async Task LoadModuleDefinitions()
{
_moduleDefinitions = _allModuleDefinitions.Where(item => item.Categories.Contains(_category)).ToList();
_packages = await PackageService.GetPackageUpdatesAsync("module");
}
private string PurchaseLink(string packagename) private string PurchaseLink(string packagename)
{ {
string link = ""; string link = "";
@ -134,10 +141,13 @@ else
{ {
if (package.ExpiryDate != null && package.ExpiryDate.Value.Date != DateTime.MaxValue.Date) if (package.ExpiryDate != null && package.ExpiryDate.Value.Date != DateTime.MaxValue.Date)
{ {
link += "<span>" + package.ExpiryDate.Value.Date.ToString("MMM dd, yyyy") + "</span><br />"; if (string.IsNullOrEmpty(package.PaymentUrl))
if (!string.IsNullOrEmpty(package.PaymentUrl))
{ {
link += "&nbsp;&nbsp;<a class=\"btn btn-primary\" style=\"text-decoration: none !important\" href=\"" + package.PaymentUrl + "\" target=\"_new\">" + SharedLocalizer["Extend"] + "</a>"; link = "<span>" + package.ExpiryDate.Value.Date.ToString("MMM dd, yyyy") + "</span>";
}
else
{
link = "<a class=\"btn btn-primary\" style=\"text-decoration: none !important\" href=\"" + package.PaymentUrl + "\" target=\"_new\">" + package.ExpiryDate.Value.Date.ToString("MMM dd, yyyy") + "</a>";
} }
} }
} }
@ -176,7 +186,7 @@ else
{ {
try try
{ {
await PackageService.DownloadPackageAsync(packagename, version, Constants.PackagesFolder); await PackageService.DownloadPackageAsync(packagename, version);
await logger.LogInformation("Module Downloaded {ModuleDefinitionName} {Version}", packagename, version); await logger.LogInformation("Module Downloaded {ModuleDefinitionName} {Version}", packagename, version);
AddModuleMessage(string.Format(Localizer["Success.Module.Install"], NavigateUrl("admin/system")), MessageType.Success); AddModuleMessage(string.Format(Localizer["Success.Module.Install"], NavigateUrl("admin/system")), MessageType.Success);
} }
@ -202,9 +212,9 @@ else
} }
} }
private void CategoryChanged(ChangeEventArgs e) private async Task CategoryChanged(ChangeEventArgs e)
{ {
_category = (string)e.Value; _category = (string)e.Value;
StateHasChanged(); await LoadModuleDefinitions();
} }
} }

View File

@ -22,12 +22,6 @@
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
public override string Title => "Export Content"; public override string Title => "Export Content";
protected override void OnParametersSet()
{
base.OnParametersSet();
base.SetModuleTitle(Localizer["ModuleTitle.Text"]);
}
private async Task ExportModule() private async Task ExportModule()
{ {
try try

View File

@ -28,11 +28,6 @@
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;
public override string Title => "Import Content"; public override string Title => "Import Content";
protected override void OnParametersSet()
{
base.OnParametersSet();
base.SetModuleTitle(Localizer["ModuleTitle.Text"]);
}
private async Task ImportModule() private async Task ImportModule()
{ {
validated = true; validated = true;

View File

@ -130,11 +130,6 @@
private string modifiedby; private string modifiedby;
private DateTime modifiedon; private DateTime modifiedon;
protected override void OnParametersSet()
{
base.OnParametersSet();
base.SetModuleTitle(Localizer["ModuleTitle.Text"]);
}
protected override void OnInitialized() protected override void OnInitialized()
{ {
_module = ModuleState.ModuleDefinition.Name; _module = ModuleState.ModuleDefinition.Name;

View File

@ -3,6 +3,7 @@
@inject NavigationManager NavigationManager @inject NavigationManager NavigationManager
@inject IPageService PageService @inject IPageService PageService
@inject IThemeService ThemeService @inject IThemeService ThemeService
@inject ISystemService SystemService
@inject IStringLocalizer<Add> Localizer @inject IStringLocalizer<Add> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer @inject IStringLocalizer<SharedResources> SharedLocalizer
@ -39,9 +40,9 @@
<Label Class="col-sm-3" For="insert" HelpText="Select the location where you would like the page to be inserted in relation to other pages" ResourceKey="Insert">Insert: </Label> <Label Class="col-sm-3" For="insert" HelpText="Select the location where you would like the page to be inserted in relation to other pages" ResourceKey="Insert">Insert: </Label>
<div class="col-sm-9"> <div class="col-sm-9">
<select id="insert" class="form-select" @bind="@_insert" required> <select id="insert" class="form-select" @bind="@_insert" required>
<option value="<<">@Localizer["AtBeginning"]</option>
@if (_children != null && _children.Count > 0) @if (_children != null && _children.Count > 0)
{ {
<option value="<<">@Localizer["AtBeginning"]</option>
<option value="<">@Localizer["Before"]</option> <option value="<">@Localizer["Before"]</option>
<option value=">">@Localizer["After"]</option> <option value=">">@Localizer["After"]</option>
} }
@ -111,8 +112,11 @@
</div> </div>
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="icon" HelpText="Optionally provide an icon class name for this page which will be displayed in the site navigation" ResourceKey="Icon">Icon: </Label> <Label Class="col-sm-3" For="icon" HelpText="Optionally provide an icon class name for this page which will be displayed in the site navigation" ResourceKey="Icon">Icon: </Label>
<div class="col-sm-9"> <div class="col-sm-8">
<input id="icon" class="form-control" @bind="@_icon" /> <InputList Value="@_icon" ValueChanged="IconChanged" DataList="@_icons" ResourceKey="Icon" ResourceType="@_iconresources" />
</div>
<div class="col-sm-1">
<i class="@_icon"></i>
</div> </div>
</div> </div>
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
@ -227,6 +231,8 @@
private RenderFragment ThemeSettingsComponent { get; set; } private RenderFragment ThemeSettingsComponent { get; set; }
private bool _refresh = false; private bool _refresh = false;
protected Page _parent = null; protected Page _parent = null;
protected Dictionary<string, string> _icons;
private string _iconresources = "";
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
@ -241,6 +247,8 @@
_parentid = _parent.PageId.ToString(); _parentid = _parent.PageId.ToString();
} }
} }
_icons = await SystemService.GetIconsAsync();
_iconresources = typeof(IconResources).FullName;
// if admin or user has edit access to parent page // if admin or user has edit access to parent page
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin) || (_parent != null && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, _parent.PermissionList))) if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin) || (_parent != null && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, _parent.PermissionList)))
@ -249,7 +257,14 @@
_themes = ThemeService.GetThemeControls(PageState.Site.Themes); _themes = ThemeService.GetThemeControls(PageState.Site.Themes);
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, _themetype); _containers = ThemeService.GetContainerControls(PageState.Site.Themes, _themetype);
_containertype = PageState.Site.DefaultContainerType; _containertype = PageState.Site.DefaultContainerType;
_children = PageState.Pages.Where(item => item.ParentId == null).ToList(); _children = new List<Page>();
foreach (Page p in PageState.Pages.Where(item => (_parentid == "-1" && item.ParentId == null) || (item.ParentId == int.Parse(_parentid))))
{
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.PermissionList))
{
_children.Add(p);
}
}
ThemeSettings(); ThemeSettings();
_initialized = true; _initialized = true;
} }
@ -272,26 +287,13 @@
{ {
_parentid = (string)e.Value; _parentid = (string)e.Value;
_children = new List<Page>(); _children = new List<Page>();
if (_parentid == "-1") foreach (Page p in PageState.Pages.Where(item => (_parentid == "-1" && item.ParentId == null) || (item.ParentId == int.Parse(_parentid))))
{
foreach (Page p in PageState.Pages.Where(item => item.ParentId == null))
{ {
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.PermissionList)) if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.PermissionList))
{ {
_children.Add(p); _children.Add(p);
} }
} }
}
else
{
foreach (Page p in PageState.Pages.Where(item => item.ParentId == int.Parse(_parentid)))
{
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.PermissionList))
{
_children.Add(p);
}
}
}
StateHasChanged(); StateHasChanged();
} }
catch (Exception ex) catch (Exception ex)
@ -482,4 +484,8 @@
NavigationManager.NavigateTo(NavigateUrl()); NavigationManager.NavigateTo(NavigateUrl());
} }
} }
private void IconChanged(string NewIcon)
{
_icon = NewIcon;
}
} }

View File

@ -5,6 +5,7 @@
@inject IPageService PageService @inject IPageService PageService
@inject IPageModuleService PageModuleService @inject IPageModuleService PageModuleService
@inject IThemeService ThemeService @inject IThemeService ThemeService
@inject ISystemService SystemService
@inject IStringLocalizer<Edit> Localizer @inject IStringLocalizer<Edit> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer @inject IStringLocalizer<SharedResources> SharedLocalizer
@ -47,13 +48,16 @@
{ {
<option value="=">&lt;@Localizer["ThisLocation.Keep"]&gt;</option> <option value="=">&lt;@Localizer["ThisLocation.Keep"]&gt;</option>
} }
<option value="<<">@Localizer["ToBeginning"]</option>
@if (_children != null && _children.Count > 0) @if (_children != null && _children.Count > 0)
{ {
<option value="<<">@Localizer["ToBeginning"]</option>
<option value="<">@Localizer["Before"]</option> <option value="<">@Localizer["Before"]</option>
<option value=">">@Localizer["After"]</option> <option value=">">@Localizer["After"]</option>
} }
@if (_parentid != _currentparentid)
{
<option value=">>">@Localizer["ToEnd"]</option> <option value=">>">@Localizer["ToEnd"]</option>
}
</select> </select>
@if (_children != null && _children.Count > 0 && (_insert == "<" || _insert == ">")) @if (_children != null && _children.Count > 0 && (_insert == "<" || _insert == ">"))
{ {
@ -123,8 +127,11 @@
</div> </div>
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="icon" HelpText="Optionally provide an icon class name for this page which will be displayed in the site navigation" ResourceKey="Icon">Icon: </Label> <Label Class="col-sm-3" For="icon" HelpText="Optionally provide an icon class name for this page which will be displayed in the site navigation" ResourceKey="Icon">Icon: </Label>
<div class="col-sm-9"> <div class="col-sm-8">
<input id="icon" class="form-control" @bind="@_icon" maxlength="50" /> <InputList Value="@_icon" ValueChanged="IconChanged" DataList="@_icons" ResourceKey="Icon" ResourceType="@_iconresources" />
</div>
<div class="col-sm-1">
<i class="@_icon"></i>
</div> </div>
</div> </div>
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
@ -205,7 +212,7 @@
<th>@Localizer["ModuleDefinition"]</th> <th>@Localizer["ModuleDefinition"]</th>
</Header> </Header>
<Row> <Row>
<td><ActionLink Action="Settings" Text="Edit" ModuleId="@context.ModuleId" Security="SecurityAccessLevel.Edit" PermissionList="@context.PermissionList" ResourceKey="ModuleSettings" /></td> <td><ActionLink Action="Settings" Text="Edit" Path="@_actualpath" ModuleId="@context.ModuleId" Security="SecurityAccessLevel.Edit" PermissionList="@context.PermissionList" ResourceKey="ModuleSettings" /></td>
<td><ActionDialog Header="Delete Module" Message="Are You Sure You Wish To Delete This Module?" Action="Delete" Security="SecurityAccessLevel.Edit" PermissionList="@context.PermissionList" Class="btn btn-danger" OnClick="@(async () => await DeleteModule(context))" ResourceKey="DeleteModule" /></td> <td><ActionDialog Header="Delete Module" Message="Are You Sure You Wish To Delete This Module?" Action="Delete" Security="SecurityAccessLevel.Edit" PermissionList="@context.PermissionList" Class="btn btn-danger" OnClick="@(async () => await DeleteModule(context))" ResourceKey="DeleteModule" /></td>
<td>@context.Title</td> <td>@context.Title</td>
<td>@context.ModuleDefinition?.Name</td> <td>@context.ModuleDefinition?.Name</td>
@ -288,6 +295,7 @@
private int _childid = -1; private int _childid = -1;
private string _isnavigation; private string _isnavigation;
private string _isclickable; private string _isclickable;
private string _actualpath;
private string _path; private string _path;
private string _url; private string _url;
private string _ispersonalizable; private string _ispersonalizable;
@ -312,14 +320,17 @@
private bool _refresh = false; private bool _refresh = false;
protected Page _page = null; protected Page _page = null;
protected Page _parent = null; protected Page _parent = null;
protected Dictionary<string, string> _icons;
private string _iconresources = "";
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
try try
{ {
_children = PageState.Pages.Where(item => item.ParentId == null).ToList();
_pageId = Int32.Parse(PageState.QueryString["id"]); _pageId = Int32.Parse(PageState.QueryString["id"]);
_page = await PageService.GetPageAsync(_pageId); _page = await PageService.GetPageAsync(_pageId);
_icons = await SystemService.GetIconsAsync();
_iconresources = Utilities.GetFullTypeName(typeof(IconResources).AssemblyQualifiedName);
if (_page != null && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, _page.PermissionList)) if (_page != null && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, _page.PermissionList))
{ {
@ -333,10 +344,19 @@
_parentid = _page.ParentId.ToString(); _parentid = _page.ParentId.ToString();
_parent = PageState.Pages.FirstOrDefault(item => item.PageId == _page.ParentId); _parent = PageState.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))))
{
if (p.PageId != _pageId && UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.PermissionList))
{
_children.Add(p);
}
}
_currentparentid = _parentid; _currentparentid = _parentid;
_isnavigation = _page.IsNavigation.ToString(); _isnavigation = _page.IsNavigation.ToString();
_isclickable = _page.IsClickable.ToString(); _isclickable = _page.IsClickable.ToString();
_path = _page.Path; _actualpath = _page.Path;
_path = _actualpath;
if (string.IsNullOrEmpty(_path)) if (string.IsNullOrEmpty(_path))
{ {
_path = "/"; _path = "/";
@ -355,10 +375,6 @@
// appearance // appearance
_title = _page.Title; _title = _page.Title;
_themetype = _page.ThemeType; _themetype = _page.ThemeType;
if (string.IsNullOrEmpty(_themetype) || ThemeService.GetTheme(PageState.Site.Themes, _themetype)?.ThemeName != ThemeService.GetTheme(PageState.Site.Themes, PageState.Site.DefaultThemeType)?.ThemeName)
{
_themetype = PageState.Site.DefaultThemeType;
}
_themes = ThemeService.GetThemeControls(PageState.Site.Themes); _themes = ThemeService.GetThemeControls(PageState.Site.Themes);
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, _themetype); _containers = ThemeService.GetContainerControls(PageState.Site.Themes, _themetype);
_containertype = _page.DefaultContainerType; _containertype = _page.DefaultContainerType;
@ -407,34 +423,14 @@
{ {
_parentid = (string)e.Value; _parentid = (string)e.Value;
_children = new List<Page>(); _children = new List<Page>();
if (_parentid == "-1") foreach (Page p in PageState.Pages.Where(item => (_parentid == "-1" && item.ParentId == null) || (item.ParentId == int.Parse(_parentid))))
{ {
foreach (Page p in PageState.Pages.Where(item => item.ParentId == null)) if (p.PageId != _pageId && UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.PermissionList))
{
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.PermissionList))
{ {
_children.Add(p); _children.Add(p);
} }
} }
} _insert = (_parentid == _currentparentid) ? "=" : ">>";
else
{
foreach (Page p in PageState.Pages.Where(item => item.ParentId == int.Parse(_parentid)))
{
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.PermissionList))
{
_children.Add(p);
}
}
}
if (_parentid == _currentparentid)
{
_insert = "=";
}
else
{
_insert = ">>";
}
StateHasChanged(); StateHasChanged();
} }
catch (Exception ex) catch (Exception ex)
@ -660,4 +656,8 @@
} }
} }
private void IconChanged(string NewIcon)
{
_icon = NewIcon;
}
} }

View File

@ -17,11 +17,17 @@ else
<th style="width: 1px;">&nbsp;</th> <th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th> <th style="width: 1px;">&nbsp;</th>
<th>@SharedLocalizer["Name"]</th> <th>@SharedLocalizer["Name"]</th>
<th>@Localizer["Title"]</th>
<th>@Localizer["Category"]</th>
<th>@Localizer["Order"]</th>
</Header> </Header>
<Row> <Row>
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.ProfileId.ToString())" Security="SecurityAccessLevel.Edit" ResourceKey="EditProfile" /></td> <td><ActionLink Action="Edit" Parameters="@($"id=" + context.ProfileId.ToString())" Security="SecurityAccessLevel.Edit" ResourceKey="EditProfile" /></td>
<td><ActionDialog Header="Delete Profile" Message="@string.Format(Localizer["Confirm.Profile.Delete"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Edit" Class="btn btn-danger" OnClick="@(async () => await DeleteProfile(context.ProfileId))" ResourceKey="DeleteProfile" /></td> <td><ActionDialog Header="Delete Profile" Message="@string.Format(Localizer["Confirm.Profile.Delete"], context.Name)" Action="Delete" Security="SecurityAccessLevel.Edit" Class="btn btn-danger" OnClick="@(async () => await DeleteProfile(context.ProfileId))" ResourceKey="DeleteProfile" /></td>
<td>@context.Name</td> <td>@context.Name</td>
<td>@context.Title</td>
<td>@context.Category</td>
<td>@context.ViewOrder</td>
</Row> </Row>
</Pager> </Pager>
} }

View File

@ -16,7 +16,7 @@
<ModuleMessage Message="@Localizer["Info.Registration.Exists"]" Type="MessageType.Info" /> <ModuleMessage Message="@Localizer["Info.Registration.Exists"]" Type="MessageType.Info" />
</Authorized> </Authorized>
<NotAuthorized> <NotAuthorized>
<ModuleMessage Message="@_passwordconstruction" Type="MessageType.Info" /> <ModuleMessage Message="@_passwordrequirements" Type="MessageType.Info" />
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate> <form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
<div class="container"> <div class="container">
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
@ -69,6 +69,7 @@ else
} }
@code { @code {
private string _passwordrequirements;
private string _username = string.Empty; private string _username = string.Empty;
private ElementReference form; private ElementReference form;
private bool validated = false; private bool validated = false;
@ -79,44 +80,16 @@ else
private string _email = string.Empty; private string _email = string.Empty;
private string _displayname = string.Empty; private string _displayname = string.Empty;
//Password construction
private string _minimumlength;
private string _uniquecharacters;
private bool _requiredigit;
private bool _requireupper;
private bool _requirelower;
private bool _requirepunctuation;
private string _passwordconstruction;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Anonymous; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Anonymous;
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId); _passwordrequirements = await UserService.GetPasswordRequirementsAsync(PageState.Site.SiteId);
_minimumlength = SettingService.GetSetting(settings, "IdentityOptions:Password:RequiredLength", "6");
_uniquecharacters = SettingService.GetSetting(settings, "IdentityOptions:Password:RequiredUniqueChars", "1");
_requiredigit = bool.Parse(SettingService.GetSetting(settings, "IdentityOptions:Password:RequireDigit", "true"));
_requireupper = bool.Parse(SettingService.GetSetting(settings, "IdentityOptions:Password:RequireUppercase", "true"));
_requirelower = bool.Parse(SettingService.GetSetting(settings, "IdentityOptions:Password:RequireLowercase", "true"));
_requirepunctuation = bool.Parse(SettingService.GetSetting(settings, "IdentityOptions:Password:RequireNonAlphanumeric", "true"));
// Replace the placeholders with the actual values of the variables
string digitRequirement = _requiredigit ? Localizer["Password.DigitRequirement"] + ", " : "";
string uppercaseRequirement = _requireupper ? Localizer["Password.UppercaseRequirement"] + ", " : "";
string lowercaseRequirement = _requirelower ? Localizer["Password.LowercaseRequirement"] + ", " : "";
string punctuationRequirement = _requirepunctuation ? Localizer["Password.PunctuationRequirement"] + ", " : "";
// Replace the placeholders with the actual values of the variables
string passwordValidationCriteriaTemplate = Localizer["Password.ValidationCriteria"];
_passwordconstruction = Localizer["Info.Registration.InvalidEmail"] + ". " + string.Format(passwordValidationCriteriaTemplate,
_minimumlength, _uniquecharacters, digitRequirement, uppercaseRequirement, lowercaseRequirement, punctuationRequirement);
} }
protected override void OnParametersSet() protected override void OnParametersSet()
{ {
_togglepassword = SharedLocalizer["ShowPassword"]; _togglepassword = SharedLocalizer["ShowPassword"];
} }
private async Task Register() private async Task Register()

View File

@ -6,6 +6,7 @@
@inject IStringLocalizer<SharedResources> SharedLocalizer @inject IStringLocalizer<SharedResources> SharedLocalizer
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate> <form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
<ModuleMessage Message="@_passwordrequirements" Type="MessageType.Info" />
<div class="container"> <div class="container">
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="username" HelpText="Your username will be populated from the link you received in the password reset notification" ResourceKey="Username">Username: </Label> <Label Class="col-sm-3" For="username" HelpText="Your username will be populated from the link you received in the password reset notification" ResourceKey="Username">Username: </Label>
@ -45,12 +46,14 @@
private string _passwordtype = "password"; private string _passwordtype = "password";
private string _togglepassword = string.Empty; private string _togglepassword = string.Empty;
private string _confirm = string.Empty; private string _confirm = string.Empty;
private string _passwordrequirements;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Anonymous; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Anonymous;
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
_togglepassword = SharedLocalizer["ShowPassword"]; _togglepassword = SharedLocalizer["ShowPassword"];
_passwordrequirements = await UserService.GetPasswordRequirementsAsync(PageState.Site.SiteId);
if (PageState.QueryString.ContainsKey("name") && PageState.QueryString.ContainsKey("token")) if (PageState.QueryString.ContainsKey("name") && PageState.QueryString.ContainsKey("token"))
{ {

View File

@ -49,7 +49,16 @@
<div class="row mb-1 align-items-center"> <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> <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="col-sm-9">
<input id="sitemap" class="form-control" @bind="@_sitemap" required disabled /> <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>
</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">
<input id="siteguid" class="form-control" @bind="@_siteguid" required disabled />
</div> </div>
</div> </div>
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
@ -257,7 +266,7 @@
<td> <td>
@if (_aliasid == -1) @if (_aliasid == -1)
{ {
<ActionDialog Action="Delete" OnClick="@(async () => await DeleteAlias(context))" ResourceKey="DeleteModule" Class="btn btn-danger" Header="Delete Alias" Message="@string.Format(Localizer["Confirm.Alias.Delete", context.Name])" /> <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>
<td>@context.Name</td> <td>@context.Name</td>
@ -348,6 +357,7 @@
private string _homepageid = "-"; private string _homepageid = "-";
private string _isdeleted; private string _isdeleted;
private string _sitemap = ""; private string _sitemap = "";
private string _siteguid = "";
private string _version = ""; private string _version = "";
private int _logofileid = -1; private int _logofileid = -1;
private FileManager _logofilemanager; private FileManager _logofilemanager;
@ -406,6 +416,7 @@
} }
_isdeleted = site.IsDeleted.ToString(); _isdeleted = site.IsDeleted.ToString();
_sitemap = PageState.Alias.Protocol + PageState.Alias.Name + "/pages/sitemap.xml"; _sitemap = PageState.Alias.Protocol + PageState.Alias.Name + "/pages/sitemap.xml";
_siteguid = site.SiteGuid;
_version = site.Version; _version = site.Version;
// appearance // appearance
@ -610,6 +621,7 @@
settings = SettingService.SetSetting(settings, "SMTPSender", _smtpsender, true); settings = SettingService.SetSetting(settings, "SMTPSender", _smtpsender, true);
settings = SettingService.SetSetting(settings, "SMTPRelay", _smtprelay, true); settings = SettingService.SetSetting(settings, "SMTPRelay", _smtprelay, true);
settings = SettingService.SetSetting(settings, "SMTPEnabled", _smtpenabled, true); settings = SettingService.SetSetting(settings, "SMTPEnabled", _smtpenabled, true);
settings = SettingService.SetSetting(settings, "SiteGuid", _siteguid, true);
settings = SettingService.SetSetting(settings, "NotificationRetention", _retention, true); settings = SettingService.SetSetting(settings, "NotificationRetention", _retention, true);
await SettingService.UpdateSiteSettingsAsync(settings, site.SiteId); await SettingService.UpdateSiteSettingsAsync(settings, site.SiteId);

View File

@ -133,12 +133,9 @@
</div> </div>
</div> </div>
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="packageservice" HelpText="Specify If The Package Service Is Enabled For Installing Modules, Themes, And Translations" ResourceKey="PackageService">Enable Package Service? </Label> <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>
<div class="col-sm-9"> <div class="col-sm-9">
<select id="packageservice" class="form-select" @bind="@_packageservice"> <input id="packageregistryurl" class="form-control" @bind="@_packageregistryurl" />
<option value="true">@SharedLocalizer["True"]</option>
<option value="false">@SharedLocalizer["False"]</option>
</select>
</div> </div>
</div> </div>
</div> </div>
@ -182,7 +179,7 @@
private string _logginglevel = string.Empty; private string _logginglevel = string.Empty;
private string _notificationlevel = string.Empty; private string _notificationlevel = string.Empty;
private string _swagger = string.Empty; private string _swagger = string.Empty;
private string _packageservice = string.Empty; private string _packageregistryurl = string.Empty;
private string _log = string.Empty; private string _log = string.Empty;
@ -213,7 +210,7 @@
_logginglevel = systeminfo["Logging:LogLevel:Default"].ToString(); _logginglevel = systeminfo["Logging:LogLevel:Default"].ToString();
_notificationlevel = systeminfo["Logging:LogLevel:Notify"].ToString(); _notificationlevel = systeminfo["Logging:LogLevel:Notify"].ToString();
_swagger = systeminfo["UseSwagger"].ToString(); _swagger = systeminfo["UseSwagger"].ToString();
_packageservice = systeminfo["PackageService"].ToString(); _packageregistryurl = systeminfo["PackageRegistryUrl"].ToString();
} }
systeminfo = await SystemService.GetSystemInfoAsync("log"); systeminfo = await SystemService.GetSystemInfoAsync("log");
@ -232,7 +229,7 @@
settings.Add("Logging:LogLevel:Default", _logginglevel); settings.Add("Logging:LogLevel:Default", _logginglevel);
settings.Add("Logging:LogLevel:Notify", _notificationlevel); settings.Add("Logging:LogLevel:Notify", _notificationlevel);
settings.Add("UseSwagger", _swagger); settings.Add("UseSwagger", _swagger);
settings.Add("PackageService", _packageservice); settings.Add("PackageRegistryUrl", _packageregistryurl);
await SystemService.UpdateSystemInfoAsync(settings); await SystemService.UpdateSystemInfoAsync(settings);
AddModuleMessage(Localizer["Success.UpdateConfig.Restart"], MessageType.Success); AddModuleMessage(Localizer["Success.UpdateConfig.Restart"], MessageType.Success);
} }

View File

@ -24,10 +24,11 @@
<div class="row justify-content-center mb-3"> <div class="row justify-content-center mb-3">
<div class="col"> <div class="col">
<div class="input-group"> <div class="input-group">
<span class="input-group-text">Product</span> <span class="input-group-text">@Localizer["Product"]</span>
<input id="search" class="form-control" placeholder="@SharedLocalizer["Search.Hint"]" @bind="@_search" /> <input id="search" class="form-control" placeholder="@SharedLocalizer["Search.Hint"]" @bind="@_search" />
<button type="button" class="btn btn-primary" @onclick="Search">@SharedLocalizer["Search"]</button> <button type="button" class="btn btn-primary" @onclick="Search">@SharedLocalizer["Search"]</button>
<button type="button" class="btn btn-secondary" @onclick="Reset">@SharedLocalizer["Reset"]</button> <button type="button" class="btn btn-secondary" @onclick="Reset">@SharedLocalizer["Reset"]</button>
<button type="button" class="btn btn-primary ms-2" @onclick="Refresh"><span class="@Icons.Reload" aria-hidden="true"></span></button>
</div> </div>
</div> </div>
</div> </div>
@ -47,7 +48,10 @@
<select class="form-select" value="@_sort" @onchange="(e => SortChanged(e))"> <select class="form-select" value="@_sort" @onchange="(e => SortChanged(e))">
<option value="popularity">@SharedLocalizer["Search.Popularity"]</option> <option value="popularity">@SharedLocalizer["Search.Popularity"]</option>
<option value="alphabetical">@SharedLocalizer["Search.Alphabetical"]</option> <option value="alphabetical">@SharedLocalizer["Search.Alphabetical"]</option>
@if (_price == "free")
{
<option value="downloads">@SharedLocalizer["Search.Downloads"]</option> <option value="downloads">@SharedLocalizer["Search.Downloads"]</option>
}
<option value="recent">@SharedLocalizer["Search.RecentlyReleased"]</option> <option value="recent">@SharedLocalizer["Search.RecentlyReleased"]</option>
@if (_price == "paid") @if (_price == "paid")
{ {
@ -60,26 +64,32 @@
<Row> <Row>
<div class="m-2 p-2 d-flex justify-content-center"> <div class="m-2 p-2 d-flex justify-content-center">
<div class="container-fluid px-0"> <div class="container-fluid px-0">
<div class="row g-0"> <div class="row g-0 mb-2">
<div class="col-6"> <div class="col-4">
@if (context.LogoFileId != null) @if (context.LogoUrl != null)
{ {
<img src="@GetLogo(context.LogoFileId.Value)" class="img-fluid" alt="@context.Name" /> <img src="@context.LogoUrl" class="img-fluid" alt="@context.Name" />
} }
else else
{ {
<img src="/package.png" class="img-fluid" alt="@context.Name" /> <img src="/package.png" class="img-fluid" alt="@context.Name" />
} }
</div> </div>
<div class="col-6 text-end"> <div class="col-8 text-end">
<small>@SharedLocalizer["Search.Version"]:</small> <strong>@context.Version</strong> <small>@SharedLocalizer["Search.Version"]:</small> <strong>@context.Version</strong>
<br /><small>@SharedLocalizer["Search.Downloads"]:</small> <strong>@(String.Format("{0:n0}", context.Downloads))</strong>
<br /><small>@SharedLocalizer["Search.Released"]:</small> <strong>@context.ReleaseDate.ToString("MM/dd/yyyy")</strong> <br /><small>@SharedLocalizer["Search.Released"]:</small> <strong>@context.ReleaseDate.ToString("MM/dd/yyyy")</strong>
@if (!string.IsNullOrEmpty(context.PackageUrl)) @if (!string.IsNullOrEmpty(context.PackageUrl))
{ {
<br /> <br /><small>@SharedLocalizer["Search.Source"]:</small> <strong>@(new Uri(context.PackageUrl).Host)</strong>
}
<small>@SharedLocalizer["Search.Source"]:</small> <strong>@(new Uri(context.PackageUrl).Host)</strong> @if (context.Price == null)
{
<br /><small>@SharedLocalizer["Search.Downloads"]:</small> <strong>@(String.Format("{0:n0}", context.Downloads))</strong>
}
else
{
<br /><small>@SharedLocalizer["From"]:</small> <strong>@context.Price.Value.ToString("$#,##0.00")</strong>
@((MarkupString)(context.TrialPeriod > 0 ? " <strong>(" + context.TrialPeriod + " Day Trial)</strong>" : ""))
} }
</div> </div>
</div> </div>
@ -88,11 +98,6 @@
<h3 style="display: inline;"><a href="@context.ProductUrl" target="_blank">@context.Name</a></h3><br /> <h3 style="display: inline;"><a href="@context.ProductUrl" target="_blank">@context.Name</a></h3><br />
<small>@SharedLocalizer["Search.By"]:</small> <strong><a href="@context.OwnerUrl" target="new">@context.Owner</a></strong><br /> <small>@SharedLocalizer["Search.By"]:</small> <strong><a href="@context.OwnerUrl" target="new">@context.Owner</a></strong><br />
@(context.Description.Length > 400 ? (context.Description.Substring(0, 400) + "...") : context.Description)<br /> @(context.Description.Length > 400 ? (context.Description.Substring(0, 400) + "...") : context.Description)<br />
@if (context.Price != null && !string.IsNullOrEmpty(context.PaymentUrl))
{
<small>@SharedLocalizer["Search.Price"]:</small> <strong>@context.Price.Value.ToString("$#,##0.00")</strong>
@((MarkupString)(context.TrialPeriod > 0 ? " <strong>(" + context.TrialPeriod + " Day Trial)</strong>" : ""))
}
<br /> <br />
@if (!string.IsNullOrEmpty(context.PackageUrl)) @if (!string.IsNullOrEmpty(context.PackageUrl))
{ {
@ -164,6 +169,7 @@
@code { @code {
private bool _initialized = false; private bool _initialized = false;
private int _page = 1;
private List<Package> _packages; private List<Package> _packages;
private string _price = "free"; private string _price = "free";
private string _sort = "popularity"; private string _sort = "popularity";
@ -210,43 +216,34 @@
HideProgressIndicator(); HideProgressIndicator();
} }
private string GetLogo(int fileid)
{
var url = ImageUrl(fileid, 100, 100);
url = (!string.IsNullOrEmpty(PageState.Alias.Path)) ? url.Substring(PageState.Alias.Path.Length + 1) : url;
return Constants.PackageRegistryUrl + url;
}
private async void PriceChanged(string price) private async void PriceChanged(string price)
{ {
_price = price; _price = price;
_sort = "popularity";
await LoadThemes(); await LoadThemes();
StateHasChanged(); StateHasChanged();
} }
private async Task Search() private async Task Search()
{
try
{ {
await LoadThemes(); await LoadThemes();
} }
catch (Exception ex)
{
await logger.LogError(ex, "Error On Search");
}
}
private async Task Reset() private async Task Reset()
{ {
try _page = 1;
{
_search = ""; _search = "";
await LoadThemes(); await LoadThemes();
} }
catch (Exception ex)
private async Task Refresh()
{ {
await logger.LogError(ex, "Error On Reset"); await LoadThemes();
} }
private void OnPageChange(int page)
{
_page = page;
} }
private async void SortChanged(ChangeEventArgs e) private async void SortChanged(ChangeEventArgs e)
@ -266,7 +263,7 @@
{ {
try try
{ {
var package = await PackageService.GetPackageAsync(packageid, version); var package = await PackageService.GetPackageAsync(packageid, version, false);
if (package != null) if (package != null)
{ {
_productname = package.Name; _productname = package.Name;
@ -295,7 +292,7 @@
{ {
try try
{ {
await PackageService.DownloadPackageAsync(_packageid, _version, Constants.PackagesFolder); await PackageService.DownloadPackageAsync(_packageid, _version);
await logger.LogInformation("Package {PackageId} {Version} Downloaded Successfully", _packageid, _version); await logger.LogInformation("Package {PackageId} {Version} Downloaded Successfully", _packageid, _version);
AddModuleMessage(string.Format(Localizer["Success.Theme.Download"], NavigateUrl("admin/system")), MessageType.Success); AddModuleMessage(string.Format(Localizer["Success.Theme.Download"], NavigateUrl("admin/system")), MessageType.Success);
_productname = ""; _productname = "";

View File

@ -79,9 +79,12 @@
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
protected override void OnInitialized() protected override void OnInitialized()
{
if (!NavigationManager.BaseUri.Contains("localhost:"))
{ {
AddModuleMessage(Localizer["Info.Theme.CreatorIntent"], MessageType.Info); AddModuleMessage(Localizer["Info.Theme.CreatorIntent"], MessageType.Info);
} }
}
protected override async Task OnParametersSetAsync() protected override async Task OnParametersSetAsync()
{ {
@ -102,7 +105,8 @@
{ {
if (IsValid(_owner) && IsValid(_theme) && _owner != _theme && _template != "-") if (IsValid(_owner) && IsValid(_theme) && _owner != _theme && _template != "-")
{ {
var theme = new Theme { Owner = _owner, Name = _theme, Template = _template, Version = _reference }; var template = _templates.FirstOrDefault(item => item.Name == _template);
var theme = new Theme { Owner = _owner, Name = _theme, Template = _template, Version = _reference, ThemeName = template.Namespace };
theme = await ThemeService.CreateThemeAsync(theme); theme = await ThemeService.CreateThemeAsync(theme);
GetLocation(); GetLocation();
AddModuleMessage(string.Format(Localizer["Success.Theme.Create"], NavigateUrl("admin/system")), MessageType.Success); AddModuleMessage(string.Format(Localizer["Success.Theme.Create"], NavigateUrl("admin/system")), MessageType.Success);
@ -142,8 +146,14 @@
if (_owner != "" && _theme != "" && _template != "-") if (_owner != "" && _theme != "" && _template != "-")
{ {
var template = _templates.FirstOrDefault(item => item.Name == _template); var template = _templates.FirstOrDefault(item => item.Name == _template);
if (!string.IsNullOrEmpty(template.Namespace))
{
_location = template.Location + template.Namespace.Replace("[Owner]", _owner).Replace("[Theme]", _theme);
}
else
{
_location = template.Location + _owner + ".Theme." + _theme; _location = template.Location + _owner + ".Theme." + _theme;
}
} }
StateHasChanged(); StateHasChanged();
} }

View File

@ -2,6 +2,7 @@
@using System.Net @using System.Net
@inherits ModuleBase @inherits ModuleBase
@inject IThemeService ThemeService @inject IThemeService ThemeService
@inject IPackageService PackageService
@inject NavigationManager NavigationManager @inject NavigationManager NavigationManager
@inject IStringLocalizer<Edit> Localizer @inject IStringLocalizer<Edit> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer @inject IStringLocalizer<SharedResources> SharedLocalizer
@ -42,9 +43,26 @@
</div> </div>
</div> </div>
<div class="row mb-1 align-items-center"> <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" ResourceKey="PackageName">Package Name: </Label> <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"> <div class="col-sm-9">
@if (!string.IsNullOrEmpty(_packagename))
{
<div class="input-group">
<input id="packagename" class="form-control" @bind="@_packagename" disabled /> <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 />
}
</div> </div>
</div> </div>
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
@ -68,7 +86,14 @@
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="license" HelpText="The license of the theme" ResourceKey="License">License: </Label> <Label Class="col-sm-3" For="license" HelpText="The license of the theme" ResourceKey="License">License: </Label>
<div class="col-sm-9"> <div class="col-sm-9">
@if (_license.StartsWith("http") || _license.StartsWith("/") || _license.StartsWith("~"))
{
<a href="@_license.Replace("~", PageState?.Alias.BaseUrl + "/Themes/" + Utilities.GetTypeName(_themeName))" class="btn btn-info" style="text-decoration: none !important" target="_new">@Localizer["View License"]</a>
}
else
{
<textarea id="license" class="form-control" @bind="@_license" rows="5" disabled></textarea> <textarea id="license" class="form-control" @bind="@_license" rows="5" disabled></textarea>
}
</div> </div>
</div> </div>
</div> </div>
@ -90,7 +115,8 @@
private string _isenabled; private string _isenabled;
private string _name; private string _name;
private string _version; private string _version;
private string _packagename; private string _packagename = "";
private string _packageurl = "";
private string _owner = ""; private string _owner = "";
private string _url = ""; private string _url = "";
private string _contact = ""; private string _contact = "";
@ -159,4 +185,27 @@
AddModuleMessage(SharedLocalizer["Message.InfoRequired"], MessageType.Warning); 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

@ -29,7 +29,7 @@ else
<th>&nbsp;</th> <th>&nbsp;</th>
</Header> </Header>
<Row> <Row>
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.ThemeId.ToString())" ResourceKey="EditModule" /></td> <td><ActionLink Action="Edit" Parameters="@($"id=" + context.ThemeId.ToString())" ResourceKey="EditTheme" /></td>
<td> <td>
@if (context.AssemblyName != Constants.ClientId) @if (context.AssemblyName != Constants.ClientId)
{ {
@ -79,7 +79,7 @@ else
try try
{ {
_themes = await ThemeService.GetThemesAsync(); _themes = await ThemeService.GetThemesAsync();
_packages = await PackageService.GetPackagesAsync("theme"); _packages = await PackageService.GetPackageUpdatesAsync("theme");
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -101,10 +101,13 @@ else
{ {
if (package.ExpiryDate != null && package.ExpiryDate.Value.Date != DateTime.MaxValue.Date) if (package.ExpiryDate != null && package.ExpiryDate.Value.Date != DateTime.MaxValue.Date)
{ {
link += "<span>" + package.ExpiryDate.Value.Date.ToString("MMM dd, yyyy") + "</span><br />"; if (string.IsNullOrEmpty(package.PaymentUrl))
if (!string.IsNullOrEmpty(package.PaymentUrl))
{ {
link += "&nbsp;&nbsp;<a class=\"btn btn-primary\" style=\"text-decoration: none !important\" href=\"" + package.PaymentUrl + "\" target=\"_new\">" + SharedLocalizer["Extend"] + "</a>"; link = "<span>" + package.ExpiryDate.Value.Date.ToString("MMM dd, yyyy") + "</span>";
}
else
{
link = "<a class=\"btn btn-primary\" style=\"text-decoration: none !important\" href=\"" + package.PaymentUrl + "\" target=\"_new\">" + package.ExpiryDate.Value.Date.ToString("MMM dd, yyyy") + "</a>";
} }
} }
} }
@ -143,7 +146,7 @@ else
{ {
try try
{ {
await PackageService.DownloadPackageAsync(packagename, version, Constants.PackagesFolder); await PackageService.DownloadPackageAsync(packagename, version);
await logger.LogInformation("Theme Downloaded {ThemeName} {Version}", packagename, version); await logger.LogInformation("Theme Downloaded {ThemeName} {Version}", packagename, version);
AddModuleMessage(string.Format(Localizer["Success.Theme.Install"], NavigateUrl("admin/system")), MessageType.Success); AddModuleMessage(string.Format(Localizer["Success.Theme.Install"], NavigateUrl("admin/system")), MessageType.Success);
} }

View File

@ -7,6 +7,8 @@
@inject IStringLocalizer<Index> Localizer @inject IStringLocalizer<Index> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer @inject IStringLocalizer<SharedResources> SharedLocalizer
@if (_initialized)
{
<TabStrip> <TabStrip>
<TabPanel Name="Download" ResourceKey="Download"> <TabPanel Name="Download" ResourceKey="Download">
@if (_package != null && _upgradeavailable) @if (_package != null && _upgradeavailable)
@ -21,7 +23,7 @@
} }
</TabPanel> </TabPanel>
<TabPanel Name="Upload" ResourceKey="Upload"> <TabPanel Name="Upload" ResourceKey="Upload">
<ModuleMessage Type="MessageType.Info" Message=@Localizer["MessgeUpgrade.Text"]></ModuleMessage> <ModuleMessage Type="MessageType.Info" Message=@Localizer["MessageUpgrade.Text"]></ModuleMessage>
<div class="container"> <div class="container">
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" HelpText="Upload A Framework Package And Then Select Upgrade" ResourceKey="Framework">Framework: </Label> <Label Class="col-sm-3" HelpText="Upload A Framework Package And Then Select Upgrade" ResourceKey="Framework">Framework: </Label>
@ -33,8 +35,10 @@
<button type="button" class="btn btn-success" @onclick="Upgrade">@SharedLocalizer["Upgrade"]</button> <button type="button" class="btn btn-success" @onclick="Upgrade">@SharedLocalizer["Upgrade"]</button>
</TabPanel> </TabPanel>
</TabStrip> </TabStrip>
}
@code { @code {
private bool _initialized = false;
private Package _package; private Package _package;
private bool _upgradeavailable = false; private bool _upgradeavailable = false;
@ -43,6 +47,12 @@
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
try try
{
if (NavigationManager.BaseUri.Contains("localhost:"))
{
AddModuleMessage(Localizer["Localhost.Text"], MessageType.Info);
}
else
{ {
List<Package> packages = await PackageService.GetPackagesAsync("framework", "", "", ""); List<Package> packages = await PackageService.GetPackagesAsync("framework", "", "", "");
if (packages != null) if (packages != null)
@ -57,6 +67,8 @@
_package = new Package { Name = Constants.PackageId, Version = Constants.Version }; _package = new Package { Name = Constants.PackageId, Version = Constants.Version };
} }
} }
_initialized = true;
}
} }
catch catch
{ {
@ -85,8 +97,8 @@
{ {
try try
{ {
await PackageService.DownloadPackageAsync(packageid, version, Constants.PackagesFolder); await PackageService.DownloadPackageAsync(packageid, version);
await PackageService.DownloadPackageAsync(Constants.UpdaterPackageId, version, Constants.PackagesFolder); await PackageService.DownloadPackageAsync(Constants.UpdaterPackageId, version);
AddModuleMessage(Localizer["Success.Framework.Download"], MessageType.Success); AddModuleMessage(Localizer["Success.Framework.Download"], MessageType.Success);
} }
catch (Exception ex) catch (Exception ex)

View File

@ -42,12 +42,6 @@
public override string Title => "Send Notification"; public override string Title => "Send Notification";
protected override void OnParametersSet()
{
base.OnParametersSet();
base.SetModuleTitle(Localizer["ModuleTitle.Text"]);
}
private async Task Send() private async Task Send()
{ {
try try

View File

@ -23,6 +23,7 @@ else
<TabPanel Name="Identity" ResourceKey="Identity"> <TabPanel Name="Identity" ResourceKey="Identity">
@if (profiles != null && settings != null) @if (profiles != null && settings != null)
{ {
<ModuleMessage Message="@_passwordrequirements" Type="MessageType.Info" />
<div class="container"> <div class="container">
<div class="row mb-1 align-items-center"> <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." ResourceKey="Username"></Label> <Label Class="col-sm-3" For="username" HelpText="Your username. Note that this field can not be modified." ResourceKey="Username"></Label>
@ -267,6 +268,7 @@ else
<br /><br /> <br /><br />
@code { @code {
private string _passwordrequirements;
private string username = string.Empty; private string username = string.Empty;
private string _password = string.Empty; private string _password = string.Empty;
private string _passwordtype = "password"; private string _passwordtype = "password";
@ -293,12 +295,11 @@ else
{ {
try try
{ {
_passwordrequirements = await UserService.GetPasswordRequirementsAsync(PageState.Site.SiteId);
_togglepassword = SharedLocalizer["ShowPassword"]; _togglepassword = SharedLocalizer["ShowPassword"];
if (PageState.Site.Settings.ContainsKey("LoginOptions:TwoFactor") && !string.IsNullOrEmpty(PageState.Site.Settings["LoginOptions:TwoFactor"])) allowtwofactor = (SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:TwoFactor", "false") == "true");
{
allowtwofactor = (PageState.Site.Settings["LoginOptions:TwoFactor"] == "true");
}
if (PageState.User != null) if (PageState.User != null)
{ {

View File

@ -55,7 +55,7 @@
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<label class="col-sm-3">@Localizer["Message"] </label> <label class="col-sm-3">@Localizer["Message"] </label>
<div class="col-sm-9"> <div class="col-sm-9">
<textarea class="form-control" @bind="@body" rows="5" readonly /> <textarea id="txtFrom" class="form-control" @bind="@body" rows="5" readonly />
</div> </div>
</div> </div>
@ -66,7 +66,7 @@
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<label class="col-sm-3">@Localizer["Message"] </label> <label class="col-sm-3">@Localizer["Message"] </label>
<div class="col-sm-9"> <div class="col-sm-9">
<textarea class="form-control" @bind="@body" rows="5" readonly /> <textarea id="txtTo" class="form-control" @bind="@body" rows="5" />
</div> </div>
</div> </div>
@ -93,7 +93,7 @@
{ {
<div class="control-group"> <div class="control-group">
<label class="control-label">@Localizer["OriginalMessage"] </label> <label class="control-label">@Localizer["OriginalMessage"] </label>
<textarea class="form-control" @bind="@reply" rows="5" readonly /> <textarea id="txtReply" class="form-control" @bind="@reply" rows="5" readonly />
</div> </div>
} }
} }
@ -110,11 +110,6 @@
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.View; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.View;
public override string Title => "View Notification"; public override string Title => "View Notification";
protected override void OnParametersSet()
{
base.OnParametersSet();
base.SetModuleTitle(Localizer["ModuleTitle.Text"]);
}
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
try try

View File

@ -12,11 +12,12 @@
<TabPanel Name="Identity" ResourceKey="Identity"> <TabPanel Name="Identity" ResourceKey="Identity">
@if (profiles != null) @if (profiles != null)
{ {
<ModuleMessage Message="@_passwordrequirements" Type="MessageType.Info" />
<div class="container"> <div class="container">
<div class="row mb-1 align-items-center"> <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> <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>
<div class="col-sm-9"> <div class="col-sm-9">
<input id="username" class="form-control" @bind="@username" /> <input id="username" class="form-control" @bind="@_username" />
</div> </div>
</div> </div>
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
@ -32,7 +33,7 @@
<Label Class="col-sm-3" For="confirm" HelpText="Please enter the password again to confirm it matches with the value above" ResourceKey="Confirm"></Label> <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="col-sm-9">
<div class="input-group"> <div class="input-group">
<input id="confirm" type="@_passwordtype" class="form-control" @bind="@confirm" autocomplete="new-password" required /> <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> <button type="button" class="btn btn-secondary" @onclick="@TogglePassword" tabindex="-1">@_togglepassword</button>
</div> </div>
</div> </div>
@ -40,17 +41,25 @@
<div class="row mb-1 align-items-center"> <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> <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"> <div class="col-sm-9">
<input id="email" class="form-control" @bind="@email" /> <input id="email" class="form-control" @bind="@_email" />
</div> </div>
</div> </div>
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="displayname" HelpText="The full name of the user" ResourceKey="DisplayName"></Label> <Label Class="col-sm-3" For="displayname" HelpText="The full name of the user" ResourceKey="DisplayName"></Label>
<div class="col-sm-9"> <div class="col-sm-9">
<input id="displayname" class="form-control" @bind="@displayname" /> <input id="displayname" class="form-control" @bind="@_displayname" />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="notify" HelpText="Indicate if new users should receive an email notification" ResourceKey="Notify">Notify? </Label>
<div class="col-sm-9">
<select id="notify" class="form-select" @bind="@_notify" required>
<option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option>
</select>
</div> </div>
</div> </div>
</div> </div>
} }
</TabPanel> </TabPanel>
<TabPanel Name="Profile" ResourceKey="Profile"> <TabPanel Name="Profile" ResourceKey="Profile">
@ -94,13 +103,15 @@
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink> <NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
@code { @code {
private string username = string.Empty; private string _passwordrequirements;
private string _username = string.Empty;
private string _password = string.Empty; private string _password = string.Empty;
private string _passwordtype = "password"; private string _passwordtype = "password";
private string _togglepassword = string.Empty; private string _togglepassword = string.Empty;
private string confirm = string.Empty; private string _confirm = string.Empty;
private string email = string.Empty; private string _email = string.Empty;
private string displayname = string.Empty; private string _displayname = string.Empty;
private string _notify = "True";
private List<Profile> profiles; private List<Profile> profiles;
private Dictionary<string, string> settings; private Dictionary<string, string> settings;
private string category = string.Empty; private string category = string.Empty;
@ -111,6 +122,7 @@
{ {
try try
{ {
_passwordrequirements = await UserService.GetPasswordRequirementsAsync(PageState.Site.SiteId);
_togglepassword = SharedLocalizer["ShowPassword"]; _togglepassword = SharedLocalizer["ShowPassword"];
profiles = await ProfileService.GetProfilesAsync(ModuleState.SiteId); profiles = await ProfileService.GetProfilesAsync(ModuleState.SiteId);
settings = new Dictionary<string, string>(); settings = new Dictionary<string, string>();
@ -136,17 +148,18 @@
{ {
try try
{ {
if (username != string.Empty && _password != string.Empty && confirm != string.Empty && email != string.Empty && ValidateProfiles()) if (_username != string.Empty && _password != string.Empty && _confirm != string.Empty && _email != string.Empty && ValidateProfiles())
{ {
if (_password == confirm) if (_password == _confirm)
{ {
var user = new User(); var user = new User();
user.SiteId = PageState.Site.SiteId; user.SiteId = PageState.Site.SiteId;
user.Username = username; user.Username = _username;
user.Password = _password; user.Password = _password;
user.Email = email; user.Email = _email;
user.DisplayName = string.IsNullOrWhiteSpace(displayname) ? username : displayname; user.DisplayName = string.IsNullOrWhiteSpace(_displayname) ? _username : _displayname;
user.PhotoFileId = null; user.PhotoFileId = null;
user.SuppressNotification = !bool.Parse(_notify);
user = await UserService.AddUserAsync(user); user = await UserService.AddUserAsync(user);
@ -158,7 +171,7 @@
} }
else else
{ {
await logger.LogError("Error Adding User {Username} {Email}", username, email); await logger.LogError("Error Adding User {Username} {Email}", _username, _email);
AddModuleMessage(Localizer["Error.User.AddCheckPass"], MessageType.Error); AddModuleMessage(Localizer["Error.User.AddCheckPass"], MessageType.Error);
} }
} }
@ -174,7 +187,7 @@
} }
catch (Exception ex) catch (Exception ex)
{ {
await logger.LogError(ex, "Error Adding User {Username} {Email} {Error}", username, email, ex.Message); await logger.LogError(ex, "Error Adding User {Username} {Email} {Error}", _username, _email, ex.Message);
AddModuleMessage(Localizer["Error.User.Add"], MessageType.Error); AddModuleMessage(Localizer["Error.User.Add"], MessageType.Error);
} }
} }

View File

@ -21,6 +21,7 @@ else
<TabPanel Name="Identity" ResourceKey="Identity"> <TabPanel Name="Identity" ResourceKey="Identity">
@if (profiles != null) @if (profiles != null)
{ {
<ModuleMessage Message="@_passwordrequirements" Type="MessageType.Info" />
<div class="container"> <div class="container">
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="username" HelpText="The unique username for a user. Note that this field can not be modified." ResourceKey="Username"></Label> <Label Class="col-sm-3" For="username" HelpText="The unique username for a user. Note that this field can not be modified." ResourceKey="Username"></Label>
@ -149,6 +150,7 @@ else
<AuditInfo CreatedBy="@createdby" CreatedOn="@createdon" ModifiedBy="@modifiedby" ModifiedOn="@modifiedon" DeletedBy="@deletedby" DeletedOn="@deletedon"></AuditInfo> <AuditInfo CreatedBy="@createdby" CreatedOn="@createdon" ModifiedBy="@modifiedby" ModifiedOn="@modifiedon" DeletedBy="@deletedby" DeletedOn="@deletedon"></AuditInfo>
@code { @code {
private string _passwordrequirements;
private int userid; private int userid;
private string username = string.Empty; private string username = string.Empty;
private string _password = string.Empty; private string _password = string.Empty;
@ -183,6 +185,7 @@ else
{ {
if (PageState.QueryString.ContainsKey("id")) if (PageState.QueryString.ContainsKey("id"))
{ {
_passwordrequirements = await UserService.GetPasswordRequirementsAsync(PageState.Site.SiteId);
_togglepassword = SharedLocalizer["ShowPassword"]; _togglepassword = SharedLocalizer["ShowPassword"];
profiles = await ProfileService.GetProfilesAsync(PageState.Site.SiteId); profiles = await ProfileService.GetProfilesAsync(PageState.Site.SiteId);
userid = Int32.Parse(PageState.QueryString["id"]); userid = Int32.Parse(PageState.QueryString["id"]);

View File

@ -20,7 +20,8 @@ else
<div class="container"> <div class="container">
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<div class="col-sm-4"> <div class="col-sm-4">
<ActionLink Action="Add" Text="Add User" Security="SecurityAccessLevel.Edit" ResourceKey="AddUser" /> <ActionLink Action="Add" Text="Add User" Security="SecurityAccessLevel.Edit" ResourceKey="AddUser" />&nbsp;
<ActionLink Text="Import Users" Class="btn btn-secondary" Action="Users" Security="SecurityAccessLevel.Admin" ResourceKey="ImportUsers"/>
</div> </div>
<div class="col-sm-4"> <div class="col-sm-4">
<input class="form-control" @bind="@_search" /> <input class="form-control" @bind="@_search" />
@ -35,9 +36,10 @@ else
<th style="width: 1px;">&nbsp;</th> <th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th> <th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th> <th style="width: 1px;">&nbsp;</th>
<th class="app-sort-th" @onclick="@(() => SortTable("Username"))">@Localizer["Username"]<i class="@(SetSortIcon("Username"))"></i></th> <th class="app-sort-th link-primary text-decoration-underline" @onclick="@(() => SortTable("Username"))">@Localizer["Username"]<i class="@(SetSortIcon("Username"))"></i></th>
<th class="app-sort-th" @onclick="@(() => SortTable("DisplayName"))">@Localizer["Name"]<i class="@(SetSortIcon("DisplayName"))"></i></th> <th class="app-sort-th link-primary text-decoration-underline" @onclick="@(() => SortTable("DisplayName"))">@Localizer["Name"]<i class="@(SetSortIcon("DisplayName"))"></i></th>
<th class="app-sort-th" @onclick="@(() => SortTable("LastLoginOn"))">@Localizer["LastLoginOn"]<i class="@(SetSortIcon("LastLoginOn"))"></i></th> <th class="app-sort-th link-primary text-decoration-underline" @onclick="@(() => SortTable("Email"))">@Localizer["Email"]<i class="@(SetSortIcon("Email"))"></i></th>
<th class="app-sort-th link-primary text-decoration-underline" @onclick="@(() => SortTable("LastLoginOn"))">@Localizer["LastLoginOn"]<i class="@(SetSortIcon("LastLoginOn"))"></i></th>
</Header> </Header>
<Row> <Row>
<td> <td>
@ -50,7 +52,8 @@ else
<ActionLink Action="Roles" Parameters="@($"id=" + context.UserId.ToString())" Security="SecurityAccessLevel.Edit" ResourceKey="Roles" /> <ActionLink Action="Roles" Parameters="@($"id=" + context.UserId.ToString())" Security="SecurityAccessLevel.Edit" ResourceKey="Roles" />
</td> </td>
<td>@context.User.Username</td> <td>@context.User.Username</td>
<td>@((MarkupString)string.Format("<a href=\"mailto:{0}\">{1}</a>", @context.User.Email, @context.User.DisplayName))</td> <td>@context.User.DisplayName</td>
<td>@((MarkupString)string.Format("<a href=\"mailto:{0}\">{1}</a>", @context.User.Email, @context.User.Email))</td>
<td>@((context.User.LastLoginOn != DateTime.MinValue) ? string.Format("{0:dd-MMM-yyyy HH:mm:ss}", context.User.LastLoginOn) : "")</td> <td>@((context.User.LastLoginOn != DateTime.MinValue) ? string.Format("{0:dd-MMM-yyyy HH:mm:ss}", context.User.LastLoginOn) : "")</td>
</Row> </Row>
</Pager> </Pager>

View File

@ -0,0 +1,69 @@
@namespace Oqtane.Modules.Admin.Users
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IUserService UserService
@inject IStringLocalizer<Users> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer
<div class="container">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="importfile" HelpText="Upload or select a tab delimited text file containing user information. The file must be in the Template format specified (Roles can be specified as a comma delimited list)." ResourceKey="ImportFile">Import File:</Label>
<div class="col-sm-9">
<FileManager Id="importfile" @ref="_filemanager" Filter="txt" />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="notify" HelpText="Indicate if new users should receive an email notification" ResourceKey="Notify">Notify? </Label>
<div class="col-sm-9">
<select id="notify" class="form-select" @bind="@_notify" required>
<option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option>
</select>
</div>
</div>
</div>
<br />
<button type="button" class="btn btn-success" @onclick="ImportUsers">@Localizer["Import"]</button>&nbsp;
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>&nbsp;
<a class="btn btn-info" href="/users.txt" target="_new">@Localizer["Template"]</a>
@code {
private FileManager _filemanager;
public override string Title => "Import Users";
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
private string _notify = "True";
private async Task ImportUsers()
{
try
{
var fileid = _filemanager.GetFileId();
if (fileid != -1)
{
ShowProgressIndicator();
var results = await UserService.ImportUsersAsync(PageState.Site.SiteId, fileid, bool.Parse(_notify));
if (bool.Parse(results["Success"]))
{
AddModuleMessage(string.Format(Localizer["Message.Import.Success"], results["Users"]), MessageType.Success);
}
else
{
AddModuleMessage(Localizer["Message.Import.Failure"], MessageType.Error);
}
HideProgressIndicator();
}
else
{
AddModuleMessage(Localizer["Message.Import.Validation"], MessageType.Warning);
}
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Importing Users {Error}", ex.Message);
AddModuleMessage(Localizer["Error.Import"], MessageType.Error);
}
}
}

View File

@ -1,6 +1,7 @@
@namespace Oqtane.Modules.Controls @namespace Oqtane.Modules.Controls
@using System.Text.Json @using System.Text.Json
@inherits LocalizableComponent @inherits LocalizableComponent
@inject IStringLocalizer<SharedResources> SharedLocalizer
@if (_visible) @if (_visible)
{ {
@ -20,7 +21,7 @@
{ {
<button type="button" class="@Class" @onclick="Confirm">@((MarkupString)_iconSpan) @Text</button> <button type="button" class="@Class" @onclick="Confirm">@((MarkupString)_iconSpan) @Text</button>
} }
<button type="button" class="btn btn-secondary" @onclick="DisplayModal">@Localize("Cancel")</button> <button type="button" class="btn btn-secondary" @onclick="DisplayModal">@SharedLocalizer["Cancel"]</button>
</div> </div>
</div> </div>
</div> </div>

View File

@ -25,6 +25,8 @@
@code { @code {
private string _text = string.Empty; private string _text = string.Empty;
private int _moduleId = -1;
private string _path = string.Empty;
private string _parameters = string.Empty; private string _parameters = string.Empty;
private string _url = string.Empty; private string _url = string.Empty;
private List<Permission> _permissions; private List<Permission> _permissions;
@ -41,10 +43,13 @@
public string Text { get; set; } // optional - defaults to Action if not specified public string Text { get; set; } // optional - defaults to Action if not specified
[Parameter] [Parameter]
public string Parameters { get; set; } // optional - querystring parameters should be in the form of "id=x&name=y" public int ModuleId { get; set; } = -1; // optional - allows the link to target a specific moduleid
[Parameter] [Parameter]
public int ModuleId { get; set; } = -1; // optional - allows the link to target a specific moduleid public string Path { get; set; } = null; // optional - allows the link to target a specific page
[Parameter]
public string Parameters { get; set; } // optional - querystring parameters should be in the form of "id=x&name=y"
[Parameter] [Parameter]
public Action OnClick { get; set; } = null; // optional - executes a method in the calling component public Action OnClick { get; set; } = null; // optional - executes a method in the calling component
@ -79,6 +84,7 @@
[Parameter] [Parameter]
public string ReturnUrl { get; set; } // optional - used to set a url to redirect to public string ReturnUrl { get; set; } // optional - used to set a url to redirect to
protected override void OnInitialized() protected override void OnInitialized()
{ {
if (!string.IsNullOrEmpty(Permissions)) if (!string.IsNullOrEmpty(Permissions))
@ -102,6 +108,18 @@
_text = string.Empty; _text = string.Empty;
} }
_moduleId = ModuleState.ModuleId;
if (ModuleId != -1)
{
_moduleId = ModuleId;
}
_path = PageState.Page.Path;
if (Path != null)
{
_path = Path;
}
if (!string.IsNullOrEmpty(Parameters)) if (!string.IsNullOrEmpty(Parameters))
{ {
_parameters = Parameters; _parameters = Parameters;
@ -133,7 +151,8 @@
_permissions = (PermissionList == null) ? ModuleState.PermissionList : PermissionList; _permissions = (PermissionList == null) ? ModuleState.PermissionList : PermissionList;
_text = Localize(nameof(Text), _text); _text = Localize(nameof(Text), _text);
_url = (ModuleId == -1) ? EditUrl(Action, _parameters) : EditUrl(ModuleId, Action, _parameters);
_url = EditUrl(_path, _moduleId, Action, _parameters);
if (!string.IsNullOrEmpty(ReturnUrl)) if (!string.IsNullOrEmpty(ReturnUrl))
{ {
_url += ((_url.Contains("?")) ? "&" : "?") + $"returnurl={WebUtility.UrlEncode(ReturnUrl)}"; _url += ((_url.Contains("?")) ? "&" : "?") + $"returnurl={WebUtility.UrlEncode(ReturnUrl)}";

View File

@ -64,12 +64,12 @@
if (!String.IsNullOrEmpty(ModifiedBy)) if (!String.IsNullOrEmpty(ModifiedBy))
{ {
_text += $" {Localizer["by"]} <b>{ModifiedBy}</b>"; _text += $" {Localizer["By"]} <b>{ModifiedBy}</b>";
} }
if (ModifiedOn != null) if (ModifiedOn != null)
{ {
_text += $" {Localizer["on"]} <b>{ModifiedOn.Value.ToString(DateTimeFormat)}</b>"; _text += $" {Localizer["On"]} <b>{ModifiedOn.Value.ToString(DateTimeFormat)}</b>";
} }
_text += "</p>"; _text += "</p>";

View File

@ -157,6 +157,15 @@
[Parameter] [Parameter]
public EventCallback<int> OnDelete { get; set; } // optional - executes a method in the calling component when a file is deleted public EventCallback<int> OnDelete { get; set; } // optional - executes a method in the calling component when a file is deleted
protected override void OnInitialized()
{
// create unique id for component
_guid = Guid.NewGuid().ToString("N");
_fileinputid = "FileInput_" + _guid;
_progressinfoid = "ProgressInfo_" + _guid;
_progressbarid = "ProgressBar_" + _guid;
}
protected override async Task OnParametersSetAsync() protected override async Task OnParametersSetAsync()
{ {
// packages folder is a framework folder for uploading installable nuget packages // packages folder is a framework folder for uploading installable nuget packages
@ -223,12 +232,6 @@
await GetFiles(); await GetFiles();
// create unique id for component
_guid = Guid.NewGuid().ToString("N");
_fileinputid = "FileInput_" + _guid;
_progressinfoid = "ProgressInfo_" + _guid;
_progressbarid = "ProgressBar_" + _guid;
_initialized = true; _initialized = true;
} }
@ -338,7 +341,8 @@
string restricted = ""; string restricted = "";
foreach (var upload in uploads) foreach (var upload in uploads)
{ {
var extension = (upload.LastIndexOf(".") != -1) ? upload.Substring(upload.LastIndexOf(".") + 1) : ""; var filename = upload.Split(':')[0];
var extension = (filename.LastIndexOf(".") != -1) ? filename.Substring(filename.LastIndexOf(".") + 1) : "";
if (!Constants.UploadableFiles.Split(',').Contains(extension.ToLower())) if (!Constants.UploadableFiles.Split(',').Contains(extension.ToLower()))
{ {
restricted += (restricted == "" ? "" : ",") + extension; restricted += (restricted == "" ? "" : ",") + extension;
@ -365,24 +369,27 @@
while (upload < uploads.Length && success) while (upload < uploads.Length && success)
{ {
success = false; success = false;
// note that progressive retry will only wait a maximum of 15 seconds which may not be long enough for very large file uploads var filename = uploads[upload].Split(':')[0];
var size = Int64.Parse(uploads[upload].Split(':')[1]);
var maxattempts = (int)Math.Ceiling(size / 500000.0) + 1; // 30 MB takes 1 minute at 5 Mbps
int attempts = 0; int attempts = 0;
while (attempts < 5 && !success) while (attempts < maxattempts && !success)
{ {
attempts += 1; attempts += 1;
Thread.Sleep(1000 * attempts); // progressive retry Thread.Sleep(1000);
if (Folder == Constants.PackagesFolder) if (Folder == Constants.PackagesFolder)
{ {
var files = await FileService.GetFilesAsync(folder); var files = await FileService.GetFilesAsync(folder);
if (files != null && files.Any(item => item.Name == uploads[upload])) if (files != null && files.Any(item => item.Name == filename))
{ {
success = true; success = true;
} }
} }
else else
{ {
var file = await FileService.GetFileAsync(int.Parse(folder), uploads[upload]); var file = await FileService.GetFileAsync(int.Parse(folder), filename);
if (file != null) if (file != null)
{ {
success = true; success = true;
@ -398,8 +405,8 @@
// reset progress indicators // reset progress indicators
if (ShowProgress) if (ShowProgress)
{ {
await interop.SetElementAttribute(_guid + "ProgressInfo", "style", "display: none;"); await interop.SetElementAttribute(_progressinfoid, "style", "display: none;");
await interop.SetElementAttribute(_guid + "ProgressBar", "style", "display: none;"); await interop.SetElementAttribute(_progressbarid, "style", "display: none;");
} }
else else
{ {
@ -430,11 +437,12 @@
else else
{ {
// set FileId to first file in upload collection // set FileId to first file in upload collection
var file = await FileService.GetFileAsync(int.Parse(folder), uploads[0]); var file = await FileService.GetFileAsync(int.Parse(folder), uploads[0].Split(":")[0]);
if (file != null) if (file != null)
{ {
FileId = file.FileId; FileId = file.FileId;
await SetImage(); await SetImage();
await OnSelect.InvokeAsync(FileId);
await OnUpload.InvokeAsync(FileId); await OnUpload.InvokeAsync(FileId);
} }
await GetFiles(); await GetFiles();
@ -481,6 +489,7 @@
await GetFiles(); await GetFiles();
FileId = -1; FileId = -1;
await SetImage(); await SetImage();
await OnSelect.InvokeAsync(FileId);
StateHasChanged(); StateHasChanged();
} }
catch (Exception ex) catch (Exception ex)

View File

@ -0,0 +1,50 @@
@namespace Oqtane.Modules.Controls
@inherits LocalizableComponent
<input type="text" value="@Value" list="@_id" class="form-control" @onchange="(e => OnChange(e))" />
<datalist id="@_id" value="@Value">
@foreach(var kvp in DataList)
{
if (!string.IsNullOrEmpty(kvp.Value))
{
<option value="@kvp.Key">@Localize(kvp.Value, kvp.Value)</option>
}
else
{
<option value="@kvp.Key">@Localize(kvp.Key, kvp.Key)</option>
}
}
</datalist>
@code {
private string _id;
[Parameter]
public string Value { get; set; }
[EditorRequired]
[Parameter]
public Dictionary<string, string> DataList { get; set; }
[EditorRequired]
[Parameter]
public EventCallback<string> ValueChanged { get; set; }
protected override void OnInitialized()
{
// create unique id for component
_id = "DataList_" + Guid.NewGuid().ToString("N");
}
protected void OnChange(ChangeEventArgs e)
{
if (!string.IsNullOrEmpty(e.Value.ToString()))
{
Value = e.Value.ToString();
if (ValueChanged.HasDelegate)
{
ValueChanged.InvokeAsync(Value);
}
}
}
}

View File

@ -5,7 +5,7 @@
<div class="row" style="margin-bottom: 50px;"> <div class="row" style="margin-bottom: 50px;">
<div class="col"> <div class="col">
<TabStrip> <TabStrip>
<TabPanel Name="Rich" Heading="Rich Text Editor"> <TabPanel Name="Rich" Heading="Rich Text Editor" ResourceKey="RichTextEditor">
@if (_richfilemanager) @if (_richfilemanager)
{ {
<FileManager @ref="_fileManager" Filter="@Constants.ImageFiles" /> <FileManager @ref="_fileManager" Filter="@Constants.ImageFiles" />

View File

@ -68,12 +68,6 @@
private List<Models.HtmlText> _htmltexts; private List<Models.HtmlText> _htmltexts;
private string _view = ""; private string _view = "";
protected override void OnParametersSet()
{
base.OnParametersSet();
base.SetModuleTitle(Localizer["ModuleTitle.Text"]);
}
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
try try

View File

@ -9,6 +9,7 @@ using Oqtane.UI;
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.JSInterop; using Microsoft.JSInterop;
using System.Linq; using System.Linq;
using System.Dynamic;
namespace Oqtane.Modules namespace Oqtane.Modules
{ {
@ -280,13 +281,17 @@ namespace Oqtane.Modules
public void SetModuleTitle(string title) public void SetModuleTitle(string title)
{ {
var obj = new { PageModuleId = ModuleState.PageModuleId, Title = title }; dynamic obj = new ExpandoObject();
obj.PageModuleId = ModuleState.PageModuleId;
obj.Title = title;
SiteState.Properties.ModuleTitle = obj; SiteState.Properties.ModuleTitle = obj;
} }
public void SetModuleVisibility(bool visible) public void SetModuleVisibility(bool visible)
{ {
var obj = new { PageModuleId = ModuleState.PageModuleId, Visible = visible }; dynamic obj = new ExpandoObject();
obj.PageModuleId = ModuleState.PageModuleId;
obj.Visible = visible;
SiteState.Properties.ModuleVisibility = obj; SiteState.Properties.ModuleVisibility = obj;
} }

View File

@ -4,7 +4,7 @@
<TargetFramework>net7.0</TargetFramework> <TargetFramework>net7.0</TargetFramework>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<Configurations>Debug;Release</Configurations> <Configurations>Debug;Release</Configurations>
<Version>4.0.2</Version> <Version>4.0.4</Version>
<Product>Oqtane</Product> <Product>Oqtane</Product>
<Authors>Shaun Walker</Authors> <Authors>Shaun Walker</Authors>
<Company>.NET Foundation</Company> <Company>.NET Foundation</Company>
@ -12,7 +12,7 @@
<Copyright>.NET Foundation</Copyright> <Copyright>.NET Foundation</Copyright>
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl> <PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl> <PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.2</PackageReleaseNotes> <PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.4</PackageReleaseNotes>
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl> <RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
<RepositoryType>Git</RepositoryType> <RepositoryType>Git</RepositoryType>
<RootNamespace>Oqtane</RootNamespace> <RootNamespace>Oqtane</RootNamespace>

View File

@ -0,0 +1,789 @@
<?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="Icon.AccountLogin" xml:space="preserve">
<value>Account Login</value>
</data>
<data name="Icon.AccountLogout" xml:space="preserve">
<value>Account Logout</value>
</data>
<data name="Icon.ActionRedo" xml:space="preserve">
<value>Action Redo</value>
</data>
<data name="Icon.ActionUndo" xml:space="preserve">
<value>Action Undo</value>
</data>
<data name="Icon.AlignCenter" xml:space="preserve">
<value>Align Center</value>
</data>
<data name="Icon.AlignLeft" xml:space="preserve">
<value>Align Left</value>
</data>
<data name="Icon.AlignRight" xml:space="preserve">
<value>Align Right</value>
</data>
<data name="Icon.Aperture" xml:space="preserve">
<value>Aperture</value>
</data>
<data name="Icon.ArrowBottom" xml:space="preserve">
<value>Arrow Bottom</value>
</data>
<data name="Icon.ArrowCircleBottom" xml:space="preserve">
<value>Arrow Circle Bottom</value>
</data>
<data name="Icon.ArrowCircleLeft" xml:space="preserve">
<value>Arrow Circle Left</value>
</data>
<data name="Icon.ArrowCircleRight" xml:space="preserve">
<value>Arrow Circle Right</value>
</data>
<data name="Icon.ArrowCircleTop" xml:space="preserve">
<value>Arrow Circle Top</value>
</data>
<data name="Icon.ArrowLeft" xml:space="preserve">
<value>Arrow Left</value>
</data>
<data name="Icon.ArrowRight" xml:space="preserve">
<value>Arrow Right</value>
</data>
<data name="Icon.ArrowThickBottom" xml:space="preserve">
<value>Arrow Thick Bottom</value>
</data>
<data name="Icon.ArrowThickLeft" xml:space="preserve">
<value>Arrow Thick Left</value>
</data>
<data name="Icon.ArrowThickRight" xml:space="preserve">
<value>Arrow Thick Right</value>
</data>
<data name="Icon.ArrowThickTop" xml:space="preserve">
<value>Arrow Thick Top</value>
</data>
<data name="Icon.ArrowTop" xml:space="preserve">
<value>Arrow Top</value>
</data>
<data name="Icon.AudioSpectrum" xml:space="preserve">
<value>Audio Spectrum</value>
</data>
<data name="Icon.Audio" xml:space="preserve">
<value>Audio</value>
</data>
<data name="Icon.Badge" xml:space="preserve">
<value>Badge</value>
</data>
<data name="Icon.Ban" xml:space="preserve">
<value>Ban</value>
</data>
<data name="Icon.BarChart" xml:space="preserve">
<value>Bar Chart</value>
</data>
<data name="Icon.Basket" xml:space="preserve">
<value>Basket</value>
</data>
<data name="Icon.BatteryEmpty" xml:space="preserve">
<value>Battery Empty</value>
</data>
<data name="Icon.BatteryFull" xml:space="preserve">
<value>Battery Full</value>
</data>
<data name="Icon.Beaker" xml:space="preserve">
<value>Beaker</value>
</data>
<data name="Icon.Bell" xml:space="preserve">
<value>Bell</value>
</data>
<data name="Icon.Bluetooth" xml:space="preserve">
<value>Bluetooth</value>
</data>
<data name="Icon.Bold" xml:space="preserve">
<value>Bold</value>
</data>
<data name="Icon.Bolt" xml:space="preserve">
<value>Bolt</value>
</data>
<data name="Icon.Book" xml:space="preserve">
<value>Book</value>
</data>
<data name="Icon.Bookmark" xml:space="preserve">
<value>Bookmark</value>
</data>
<data name="Icon.Box" xml:space="preserve">
<value>Box</value>
</data>
<data name="Icon.Briefcase" xml:space="preserve">
<value>Briefcase</value>
</data>
<data name="Icon.BritishPound" xml:space="preserve">
<value>British Pound</value>
</data>
<data name="Icon.Browser" xml:space="preserve">
<value>Browser</value>
</data>
<data name="Icon.Brush" xml:space="preserve">
<value>Brush</value>
</data>
<data name="Icon.Bug" xml:space="preserve">
<value>Bug</value>
</data>
<data name="Icon.Bullhorn" xml:space="preserve">
<value>Bullhorn</value>
</data>
<data name="Icon.Calculator" xml:space="preserve">
<value>Calculator</value>
</data>
<data name="Icon.Calendar" xml:space="preserve">
<value>Calendar</value>
</data>
<data name="Icon.CameraSlr" xml:space="preserve">
<value>Camera Slr</value>
</data>
<data name="Icon.CaretBottom" xml:space="preserve">
<value>Caret Bottom</value>
</data>
<data name="Icon.CaretLeft" xml:space="preserve">
<value>Caret Left</value>
</data>
<data name="Icon.CaretRight" xml:space="preserve">
<value>Caret Right</value>
</data>
<data name="Icon.CaretTop" xml:space="preserve">
<value>Caret Top</value>
</data>
<data name="Icon.Cart" xml:space="preserve">
<value>Cart</value>
</data>
<data name="Icon.Chat" xml:space="preserve">
<value>Chat</value>
</data>
<data name="Icon.Check" xml:space="preserve">
<value>Check</value>
</data>
<data name="Icon.ChevronBottom" xml:space="preserve">
<value>Chevron Bottom</value>
</data>
<data name="Icon.ChevronLeft" xml:space="preserve">
<value>Chevron Left</value>
</data>
<data name="Icon.ChevronRight" xml:space="preserve">
<value>Chevron Right</value>
</data>
<data name="Icon.ChevronTop" xml:space="preserve">
<value>Chevron Top</value>
</data>
<data name="Icon.CircleCheck" xml:space="preserve">
<value>Circle Check</value>
</data>
<data name="Icon.CircleX" xml:space="preserve">
<value>Circle X</value>
</data>
<data name="Icon.Clipboard" xml:space="preserve">
<value>Clipboard</value>
</data>
<data name="Icon.Clock" xml:space="preserve">
<value>Clock</value>
</data>
<data name="Icon.CloudDownload" xml:space="preserve">
<value>Cloud Download</value>
</data>
<data name="Icon.CloudUpload" xml:space="preserve">
<value>Cloud Upload</value>
</data>
<data name="Icon.Cloud" xml:space="preserve">
<value>Cloud</value>
</data>
<data name="Icon.Cloudy" xml:space="preserve">
<value>Cloudy</value>
</data>
<data name="Icon.Code" xml:space="preserve">
<value>Code</value>
</data>
<data name="Icon.Cog" xml:space="preserve">
<value>Cog</value>
</data>
<data name="Icon.CollapseDown" xml:space="preserve">
<value>Collapse Down</value>
</data>
<data name="Icon.CollapseLeft" xml:space="preserve">
<value>Collapse Left</value>
</data>
<data name="Icon.CollapseRight" xml:space="preserve">
<value>Collapse Right</value>
</data>
<data name="Icon.CollapseUp" xml:space="preserve">
<value>Collapse Up</value>
</data>
<data name="Icon.Command" xml:space="preserve">
<value>Command</value>
</data>
<data name="Icon.CommentSquare" xml:space="preserve">
<value>Comment Square</value>
</data>
<data name="Icon.Compass" xml:space="preserve">
<value>Compass</value>
</data>
<data name="Icon.Contrast" xml:space="preserve">
<value>Contrast</value>
</data>
<data name="Icon.Copywriting" xml:space="preserve">
<value>Copywriting</value>
</data>
<data name="Icon.CreditCard" xml:space="preserve">
<value>Credit Card</value>
</data>
<data name="Icon.Crop" xml:space="preserve">
<value>Crop</value>
</data>
<data name="Icon.Dashboard" xml:space="preserve">
<value>Dashboard</value>
</data>
<data name="Icon.DataTransferDownload" xml:space="preserve">
<value>Data Transfer Download</value>
</data>
<data name="Icon.DataTransferUpload" xml:space="preserve">
<value>Data Transfer Upload</value>
</data>
<data name="Icon.Delete" xml:space="preserve">
<value>Delete</value>
</data>
<data name="Icon.Dial" xml:space="preserve">
<value>Dial</value>
</data>
<data name="Icon.Document" xml:space="preserve">
<value>Document</value>
</data>
<data name="Icon.Dollar" xml:space="preserve">
<value>Dollar</value>
</data>
<data name="Icon.DoubleQuoteSansLeft" xml:space="preserve">
<value>Double Quote Sans Left</value>
</data>
<data name="Icon.DoubleQuoteSansRight" xml:space="preserve">
<value>Double Quote Sans Right</value>
</data>
<data name="Icon.DoubleQuoteSerifLeft" xml:space="preserve">
<value>Double Quote Serif Left</value>
</data>
<data name="Icon.DoubleQuoteSerifRight" xml:space="preserve">
<value>Double Quote Serif Right</value>
</data>
<data name="Icon.Droplet" xml:space="preserve">
<value>Droplet</value>
</data>
<data name="Icon.Eject" xml:space="preserve">
<value>Eject</value>
</data>
<data name="Icon.Elevator" xml:space="preserve">
<value>Elevator</value>
</data>
<data name="Icon.Ellipses" xml:space="preserve">
<value>Ellipses</value>
</data>
<data name="Icon.EnvelopeClosed" xml:space="preserve">
<value>Envelope Closed</value>
</data>
<data name="Icon.EnvelopeOpen" xml:space="preserve">
<value>Envelope Open</value>
</data>
<data name="Icon.Euro" xml:space="preserve">
<value>Euro</value>
</data>
<data name="Icon.Excerpt" xml:space="preserve">
<value>Excerpt</value>
</data>
<data name="Icon.ExpandDown" xml:space="preserve">
<value>Expand Down</value>
</data>
<data name="Icon.ExpandLeft" xml:space="preserve">
<value>Expand Left</value>
</data>
<data name="Icon.ExpandRight" xml:space="preserve">
<value>Expand Right</value>
</data>
<data name="Icon.ExpandUp" xml:space="preserve">
<value>Expand Up</value>
</data>
<data name="Icon.ExternalLink" xml:space="preserve">
<value>External Link</value>
</data>
<data name="Icon.Eye" xml:space="preserve">
<value>Eye</value>
</data>
<data name="Icon.Eyedropper" xml:space="preserve">
<value>Eyedropper</value>
</data>
<data name="Icon.File" xml:space="preserve">
<value>File</value>
</data>
<data name="Icon.Fire" xml:space="preserve">
<value>Fire</value>
</data>
<data name="Icon.Flag" xml:space="preserve">
<value>Flag</value>
</data>
<data name="Icon.Flash" xml:space="preserve">
<value>Flash</value>
</data>
<data name="Icon.Folder" xml:space="preserve">
<value>Folder</value>
</data>
<data name="Icon.Fork" xml:space="preserve">
<value>Fork</value>
</data>
<data name="Icon.FullscreenEnter" xml:space="preserve">
<value>Fullscreen Enter</value>
</data>
<data name="Icon.FullscreenExit" xml:space="preserve">
<value>Fullscreen Exit</value>
</data>
<data name="Icon.Globe" xml:space="preserve">
<value>Globe</value>
</data>
<data name="Icon.Graph" xml:space="preserve">
<value>Graph</value>
</data>
<data name="Icon.GridFourUp" xml:space="preserve">
<value>Grid Four Up</value>
</data>
<data name="Icon.GridThreeUp" xml:space="preserve">
<value>Grid Three Up</value>
</data>
<data name="Icon.GridTwoUp" xml:space="preserve">
<value>Grid Two Up</value>
</data>
<data name="Icon.HardDrive" xml:space="preserve">
<value>Hard Drive</value>
</data>
<data name="Icon.Header" xml:space="preserve">
<value>Header</value>
</data>
<data name="Icon.Headphones" xml:space="preserve">
<value>Headphones</value>
</data>
<data name="Icon.Heart" xml:space="preserve">
<value>Heart</value>
</data>
<data name="Icon.Home" xml:space="preserve">
<value>Home</value>
</data>
<data name="Icon.Image" xml:space="preserve">
<value>Image</value>
</data>
<data name="Icon.Inbox" xml:space="preserve">
<value>Inbox</value>
</data>
<data name="Icon.Infinity" xml:space="preserve">
<value>Infinity</value>
</data>
<data name="Icon.Info" xml:space="preserve">
<value>Info</value>
</data>
<data name="Icon.Italic" xml:space="preserve">
<value>Italic</value>
</data>
<data name="Icon.JustifyCenter" xml:space="preserve">
<value>Justify Center</value>
</data>
<data name="Icon.JustifyLeft" xml:space="preserve">
<value>Justify Left</value>
</data>
<data name="Icon.JustifyRight" xml:space="preserve">
<value>Justify Right</value>
</data>
<data name="Icon.Key" xml:space="preserve">
<value>Key</value>
</data>
<data name="Icon.Laptop" xml:space="preserve">
<value>Laptop</value>
</data>
<data name="Icon.Layers" xml:space="preserve">
<value>Layers</value>
</data>
<data name="Icon.Lightbulb" xml:space="preserve">
<value>Lightbulb</value>
</data>
<data name="Icon.LinkBroken" xml:space="preserve">
<value>Link Broken</value>
</data>
<data name="Icon.LinkIntact" xml:space="preserve">
<value>Link Intact</value>
</data>
<data name="Icon.ListRich" xml:space="preserve">
<value>List Rich</value>
</data>
<data name="Icon.List" xml:space="preserve">
<value>List</value>
</data>
<data name="Icon.Location" xml:space="preserve">
<value>Location</value>
</data>
<data name="Icon.LockLocked" xml:space="preserve">
<value>Lock Locked</value>
</data>
<data name="Icon.LockUnlocked" xml:space="preserve">
<value>Lock Unlocked</value>
</data>
<data name="Icon.LoopCircular" xml:space="preserve">
<value>Loop Circular</value>
</data>
<data name="Icon.LoopSquare" xml:space="preserve">
<value>Loop Square</value>
</data>
<data name="Icon.Loop" xml:space="preserve">
<value>Loop</value>
</data>
<data name="Icon.MagnifyingGlass" xml:space="preserve">
<value>Magnifying Glass</value>
</data>
<data name="Icon.MapMarker" xml:space="preserve">
<value>Map Marker</value>
</data>
<data name="Icon.Map" xml:space="preserve">
<value>Map</value>
</data>
<data name="Icon.MediaPause" xml:space="preserve">
<value>Media Pause</value>
</data>
<data name="Icon.MediaPlay" xml:space="preserve">
<value>Media Play</value>
</data>
<data name="Icon.MediaRecord" xml:space="preserve">
<value>Media Record</value>
</data>
<data name="Icon.MediaSkipBackward" xml:space="preserve">
<value>Media Skip Backward</value>
</data>
<data name="Icon.MediaSkipForward" xml:space="preserve">
<value>Media Skip Forward</value>
</data>
<data name="Icon.MediaStepBackward" xml:space="preserve">
<value>Media Step Backward</value>
</data>
<data name="Icon.MediaStepForward" xml:space="preserve">
<value>Media Step Forward</value>
</data>
<data name="Icon.MediaStop" xml:space="preserve">
<value>Media Stop</value>
</data>
<data name="Icon.MedicalCross" xml:space="preserve">
<value>Medical Cross</value>
</data>
<data name="Icon.Menu" xml:space="preserve">
<value>Menu</value>
</data>
<data name="Icon.Microphone" xml:space="preserve">
<value>Microphone</value>
</data>
<data name="Icon.Minus" xml:space="preserve">
<value>Minus</value>
</data>
<data name="Icon.Monitor" xml:space="preserve">
<value>Monitor</value>
</data>
<data name="Icon.Moon" xml:space="preserve">
<value>Moon</value>
</data>
<data name="Icon.Move" xml:space="preserve">
<value>Move</value>
</data>
<data name="Icon.MusicalNote" xml:space="preserve">
<value>Musical Note</value>
</data>
<data name="Icon.Paperclip" xml:space="preserve">
<value>Paperclip</value>
</data>
<data name="Icon.Pencil" xml:space="preserve">
<value>Pencil</value>
</data>
<data name="Icon.People" xml:space="preserve">
<value>People</value>
</data>
<data name="Icon.Person" xml:space="preserve">
<value>Person</value>
</data>
<data name="Icon.Phone" xml:space="preserve">
<value>Phone</value>
</data>
<data name="Icon.PieChart" xml:space="preserve">
<value>Pie Chart</value>
</data>
<data name="Icon.Pin" xml:space="preserve">
<value>Pin</value>
</data>
<data name="Icon.PlayCircle" xml:space="preserve">
<value>Play Circle</value>
</data>
<data name="Icon.Plus" xml:space="preserve">
<value>Plus</value>
</data>
<data name="Icon.PowerStandby" xml:space="preserve">
<value>Power Standby</value>
</data>
<data name="Icon.Print" xml:space="preserve">
<value>Print</value>
</data>
<data name="Icon.Project" xml:space="preserve">
<value>Project</value>
</data>
<data name="Icon.Pulse" xml:space="preserve">
<value>Pulse</value>
</data>
<data name="Icon.PuzzlePiece" xml:space="preserve">
<value>Puzzle Piece</value>
</data>
<data name="Icon.QuestionMark" xml:space="preserve">
<value>Question Mark</value>
</data>
<data name="Icon.Rain" xml:space="preserve">
<value>Rain</value>
</data>
<data name="Icon.Random" xml:space="preserve">
<value>Random</value>
</data>
<data name="Icon.Reload" xml:space="preserve">
<value>Reload</value>
</data>
<data name="Icon.ResizeBoth" xml:space="preserve">
<value>Resize Both</value>
</data>
<data name="Icon.ResizeHeight" xml:space="preserve">
<value>Resize Height</value>
</data>
<data name="Icon.ResizeWidth" xml:space="preserve">
<value>Resize Width</value>
</data>
<data name="Icon.RssAlt" xml:space="preserve">
<value>Rss Alt</value>
</data>
<data name="Icon.Rss" xml:space="preserve">
<value>Rss</value>
</data>
<data name="Icon.Script" xml:space="preserve">
<value>Script</value>
</data>
<data name="Icon.ShareBoxed" xml:space="preserve">
<value>Share Boxed</value>
</data>
<data name="Icon.Share" xml:space="preserve">
<value>Share</value>
</data>
<data name="Icon.Shield" xml:space="preserve">
<value>Shield</value>
</data>
<data name="Icon.Signal" xml:space="preserve">
<value>Signal</value>
</data>
<data name="Icon.Signpost" xml:space="preserve">
<value>Signpost</value>
</data>
<data name="Icon.SortAscending" xml:space="preserve">
<value>Sort Ascending</value>
</data>
<data name="Icon.SortDescending" xml:space="preserve">
<value>Sort Descending</value>
</data>
<data name="Icon.Spreadsheet" xml:space="preserve">
<value>Spreadsheet</value>
</data>
<data name="Icon.Star" xml:space="preserve">
<value>Star</value>
</data>
<data name="Icon.Sun" xml:space="preserve">
<value>Sun</value>
</data>
<data name="Icon.Tablet" xml:space="preserve">
<value>Tablet</value>
</data>
<data name="Icon.Tag" xml:space="preserve">
<value>Tag</value>
</data>
<data name="Icon.Tags" xml:space="preserve">
<value>Tags</value>
</data>
<data name="Icon.Target" xml:space="preserve">
<value>Target</value>
</data>
<data name="Icon.Task" xml:space="preserve">
<value>Task</value>
</data>
<data name="Icon.Terminal" xml:space="preserve">
<value>Terminal</value>
</data>
<data name="Icon.Text" xml:space="preserve">
<value>Text</value>
</data>
<data name="Icon.ThumbDown" xml:space="preserve">
<value>Thumb Down</value>
</data>
<data name="Icon.ThumbUp" xml:space="preserve">
<value>Thumb Up</value>
</data>
<data name="Icon.Timer" xml:space="preserve">
<value>Timer</value>
</data>
<data name="Icon.Transfer" xml:space="preserve">
<value>Transfer</value>
</data>
<data name="Icon.Trash" xml:space="preserve">
<value>Trash</value>
</data>
<data name="Icon.Underline" xml:space="preserve">
<value>Underline</value>
</data>
<data name="Icon.VerticalAlignBottom" xml:space="preserve">
<value>Vertical Align Bottom</value>
</data>
<data name="Icon.VerticalAlignCenter" xml:space="preserve">
<value>Vertical Align Center</value>
</data>
<data name="Icon.VerticalAlignTop" xml:space="preserve">
<value>Vertical Align Top</value>
</data>
<data name="Icon.Video" xml:space="preserve">
<value>Video</value>
</data>
<data name="Icon.VolumeHigh" xml:space="preserve">
<value>Volume High</value>
</data>
<data name="Icon.VolumeLow" xml:space="preserve">
<value>Volume Low</value>
</data>
<data name="Icon.VolumeOff" xml:space="preserve">
<value>Volume Off</value>
</data>
<data name="Icon.Warning" xml:space="preserve">
<value>Warning</value>
</data>
<data name="Icon.Wifi" xml:space="preserve">
<value>Wifi</value>
</data>
<data name="Icon.Wrench" xml:space="preserve">
<value>Wrench</value>
</data>
<data name="Icon.X" xml:space="preserve">
<value>X</value>
</data>
<data name="Icon.Yen" xml:space="preserve">
<value>Yen</value>
</data>
<data name="Icon.ZoomIn" xml:space="preserve">
<value>Zoom In</value>
</data>
<data name="Icon.ZoomOut" xml:space="preserve">
<value>Zoom Out</value>
</data>
</root>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<root> <root>
<!-- <!--
Microsoft ResX Schema Microsoft ResX Schema
@ -150,7 +150,7 @@
<data name="Description.Text" xml:space="preserve"> <data name="Description.Text" xml:space="preserve">
<value>Description:</value> <value>Description:</value>
</data> </data>
<data name="ModuleTitle.Text" xml:space="preserve"> <data name="File Management" xml:space="preserve">
<value>File Management</value> <value>File Management</value>
</data> </data>
</root> </root>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<root> <root>
<!-- <!--
Microsoft ResX Schema Microsoft ResX Schema
@ -178,7 +178,7 @@
<value>Capacity:</value> <value>Capacity:</value>
</data> </data>
<data name="ImageSizes.HelpText" xml:space="preserve"> <data name="ImageSizes.HelpText" xml:space="preserve">
<value>Enter a list of image sizes which can be generated dynamically from uploaded images (ie. 200x200,x200,200x)</value> <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>
</data> </data>
<data name="ImageSizes.Text" xml:space="preserve"> <data name="ImageSizes.Text" xml:space="preserve">
<value>Image Sizes:</value> <value>Image Sizes:</value>
@ -192,7 +192,7 @@
<data name="Public" xml:space="preserve"> <data name="Public" xml:space="preserve">
<value>Public</value> <value>Public</value>
</data> </data>
<data name="ModuleTitle.Text" xml:space="preserve"> <data name="Folder Management" xml:space="preserve">
<value>Folder Management</value> <value>Folder Management</value>
</data> </data>
</root> </root>

View File

@ -124,7 +124,7 @@
<value>Error Loading Packages</value> <value>Error Loading Packages</value>
</data> </data>
<data name="Success.Module.Download" xml:space="preserve"> <data name="Success.Module.Download" xml:space="preserve">
<value>Module Package Saved Successfully. You Must &lt;a href={0}&gt;Restart&lt;/a&gt; Your Application To Complete The Installation.</value> <value>Module Package Downloaded Successfully. You Must &lt;a href={0}&gt;Restart&lt;/a&gt; Your Application To Complete The Installation.</value>
</data> </data>
<data name="Error.Module.Download" xml:space="preserve"> <data name="Error.Module.Download" xml:space="preserve">
<value>Error Downloading Module</value> <value>Error Downloading Module</value>
@ -141,7 +141,7 @@
<data name="Upload.Heading" xml:space="preserve"> <data name="Upload.Heading" xml:space="preserve">
<value>Upload</value> <value>Upload</value>
</data> </data>
<data name="Product.Text" xml:space="preserve"> <data name="Product" xml:space="preserve">
<value>Product</value> <value>Product</value>
</data> </data>
</root> </root>

View File

@ -132,6 +132,9 @@
<data name="Message.Require.ValidName" xml:space="preserve"> <data name="Message.Require.ValidName" xml:space="preserve">
<value>You Must Provide A Valid Owner Name And Module Name ( ie. No Punctuation Or Spaces And The Values Cannot Be The Same ) And Choose A Template</value> <value>You Must Provide A Valid Owner Name And Module Name ( ie. No Punctuation Or Spaces And The Values Cannot Be The Same ) And Choose A Template</value>
</data> </data>
<data name="Message.Require.ValidDescription" xml:space="preserve">
<value>You Must Provide A Valid Description (ie. No Punctuation)</value>
</data>
<data name="OwnerName.HelpText" xml:space="preserve"> <data name="OwnerName.HelpText" xml:space="preserve">
<value>Enter the name of the organization who is developing this module. It should not contain spaces or punctuation.</value> <value>Enter the name of the organization who is developing this module. It should not contain spaces or punctuation.</value>
</data> </data>

View File

@ -196,7 +196,7 @@
<value>Information</value> <value>Information</value>
</data> </data>
<data name="PackageName.HelpText" xml:space="preserve"> <data name="PackageName.HelpText" xml:space="preserve">
<value>The unique name of the package from which this module was installed</value> <value>The unique name of the package from which this module was installed. This value must be specified within the module's IModule interface specification.</value>
</data> </data>
<data name="PackageName.Text" xml:space="preserve"> <data name="PackageName.Text" xml:space="preserve">
<value>Package Name:</value> <value>Package Name:</value>
@ -225,4 +225,19 @@
<data name="IsEnabled.Text" xml:space="preserve"> <data name="IsEnabled.Text" xml:space="preserve">
<value>Enabled?</value> <value>Enabled?</value>
</data> </data>
<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> </root>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<root> <root>
<!-- <!--
Microsoft ResX Schema Microsoft ResX Schema
@ -132,7 +132,7 @@
<data name="Success.Content.Export" xml:space="preserve"> <data name="Success.Content.Export" xml:space="preserve">
<value>Content Exported Successfully</value> <value>Content Exported Successfully</value>
</data> </data>
<data name="ModuleTitle.Text" xml:space="preserve"> <data name="Export Content" xml:space="preserve">
<value>Export Content</value> <value>Export Content</value>
</data> </data>
</root> </root>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<root> <root>
<!-- <!--
Microsoft ResX Schema Microsoft ResX Schema
@ -138,7 +138,7 @@
<data name="Message.Required.ImportContent" xml:space="preserve"> <data name="Message.Required.ImportContent" xml:space="preserve">
<value>You Must Enter Some Content To Import</value> <value>You Must Enter Some Content To Import</value>
</data> </data>
<data name="ModuleTitle.Text" xml:space="preserve"> <data name="Import Content" xml:space="preserve">
<value>Import Content</value> <value>Import Content</value>
</data> </data>
</root> </root>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<root> <root>
<!-- <!--
Microsoft ResX Schema Microsoft ResX Schema
@ -156,7 +156,7 @@
<data name="Module.Text" xml:space="preserve"> <data name="Module.Text" xml:space="preserve">
<value>Module:</value> <value>Module:</value>
</data> </data>
<data name="ModuleTitle.Text" xml:space="preserve"> <data name="Module Settings" xml:space="preserve">
<value>Module Settings</value> <value>Module Settings</value>
</data> </data>
</root> </root>

View File

@ -138,4 +138,13 @@
<data name="EditProfile.Text" xml:space="preserve"> <data name="EditProfile.Text" xml:space="preserve">
<value>Edit</value> <value>Edit</value>
</data> </data>
<data name="Category" xml:space="preserve">
<value>Category</value>
</data>
<data name="Order" xml:space="preserve">
<value>Order</value>
</data>
<data name="Title" xml:space="preserve">
<value>Title</value>
</data>
</root> </root>

View File

@ -136,7 +136,7 @@
<value>User Account Created. Please Check Your Email For Verification Instructions.</value> <value>User Account Created. Please Check Your Email For Verification Instructions.</value>
</data> </data>
<data name="Error.User.AddInfo" xml:space="preserve"> <data name="Error.User.AddInfo" xml:space="preserve">
<value>Error Adding User. Please Ensure Password Meets Complexity Requirements And Username Is Not Already In Use.</value> <value>Error Adding User. Please Ensure Password Meets Complexity Requirements And Username And Email Is Not Already In Use.</value>
</data> </data>
<data name="Message.Password.NoMatch" xml:space="preserve"> <data name="Message.Password.NoMatch" xml:space="preserve">
<value>Passwords Entered Do Not Match</value> <value>Passwords Entered Do Not Match</value>
@ -177,19 +177,4 @@
<data name="Username.Text" xml:space="preserve"> <data name="Username.Text" xml:space="preserve">
<value>Username:</value> <value>Username:</value>
</data> </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>
</data>
<data name="Password.DigitRequirement" xml:space="preserve">
<value>At Least One Digit</value>
</data>
<data name="Password.LowercaseRequirement" xml:space="preserve">
<value>At Least One Lowercase Letter</value>
</data>
<data name="Password.PunctuationRequirement" xml:space="preserve">
<value>At Least One Punctuation Mark</value>
</data>
<data name="Password.UppercaseRequirement" xml:space="preserve">
<value>At Least One Uppercase Letter</value>
</data>
</root> </root>

View File

@ -312,6 +312,9 @@
<data name="Database.HelpText" xml:space="preserve"> <data name="Database.HelpText" xml:space="preserve">
<value>The type of database</value> <value>The type of database</value>
</data> </data>
<data name="DeleteSite.Header" xml:space="preserve">
<value>Delete Site</value>
</data>
<data name="DeleteSite.Text" xml:space="preserve"> <data name="DeleteSite.Text" xml:space="preserve">
<value>Delete Site</value> <value>Delete Site</value>
</data> </data>
@ -381,4 +384,22 @@
<data name="Version.Text" xml:space="preserve"> <data name="Version.Text" xml:space="preserve">
<value>Version:</value> <value>Version:</value>
</data> </data>
<data name="DeleteAlias.Text" xml:space="preserve">
<value>Delete</value>
</data>
<data name="DeleteAlias.Header" xml:space="preserve">
<value>Delete Alias</value>
</data>
<data name="SiteGuid.HelpText" xml:space="preserve">
<value>The Unique Identifier For The Site</value>
</data>
<data name="SiteGuid.Text" xml:space="preserve">
<value>ID:</value>
</data>
<data name="Retention.HelpText" xml:space="preserve">
<value>Number of days of notifications to retain</value>
</data>
<data name="Retention.Text" xml:space="preserve">
<value>Retention (Days):</value>
</data>
</root> </root>

View File

@ -219,11 +219,11 @@
<data name="Success.Register" xml:space="preserve"> <data name="Success.Register" xml:space="preserve">
<value>You Have Been Successfully Registered For Updates</value> <value>You Have Been Successfully Registered For Updates</value>
</data> </data>
<data name="PackageService.HelpText" xml:space="preserve"> <data name="PackageManager.HelpText" xml:space="preserve">
<value>Specify If The Package Service Is Enabled For Installing Modules, Themes, And Translations</value> <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>
</data> </data>
<data name="PackageService.Text" xml:space="preserve"> <data name="PackageManager.Text" xml:space="preserve">
<value>Package Service Enabled?</value> <value>Package Manager:</value>
</data> </data>
<data name="Swagger.HelpText" xml:space="preserve"> <data name="Swagger.HelpText" xml:space="preserve">
<value>Specify If Swagger Is Enabled For Your Server API</value> <value>Specify If Swagger Is Enabled For Your Server API</value>

View File

@ -124,7 +124,7 @@
<value>Theme: </value> <value>Theme: </value>
</data> </data>
<data name="Success.Theme.Download" xml:space="preserve"> <data name="Success.Theme.Download" xml:space="preserve">
<value>Theme Package Saved Successfully. You Must &lt;a href={0}&gt;Restart&lt;/a&gt; Your Application To Complete The Installation.</value> <value>Theme Package Downloaded Successfully. You Must &lt;a href={0}&gt;Restart&lt;/a&gt; Your Application To Complete The Installation.</value>
</data> </data>
<data name="Error.Theme.Download" xml:space="preserve"> <data name="Error.Theme.Download" xml:space="preserve">
<value>Error Downloading Theme</value> <value>Error Downloading Theme</value>
@ -141,4 +141,7 @@
<data name="Upload.Heading" xml:space="preserve"> <data name="Upload.Heading" xml:space="preserve">
<value>Upload</value> <value>Upload</value>
</data> </data>
<data name="Product" xml:space="preserve">
<value>Product</value>
</data>
</root> </root>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<root> <root>
<!-- <!--
Microsoft ResX Schema Microsoft ResX Schema
@ -163,7 +163,7 @@
<value>The license of the theme</value> <value>The license of the theme</value>
</data> </data>
<data name="PackageName.HelpText" xml:space="preserve"> <data name="PackageName.HelpText" xml:space="preserve">
<value>The unique name of the package from which this module was installed</value> <value>The unique name of the package from which this theme was installed. This value must be specified within the themee's ITheme interface specification.</value>
</data> </data>
<data name="PackageName.Text" xml:space="preserve"> <data name="PackageName.Text" xml:space="preserve">
<value>Package Name:</value> <value>Package Name:</value>
@ -177,4 +177,19 @@
<data name="IsEnabled.Text" xml:space="preserve"> <data name="IsEnabled.Text" xml:space="preserve">
<value>Enabled?</value> <value>Enabled?</value>
</data> </data>
<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> </root>

View File

@ -138,6 +138,9 @@
<data name="DeleteTheme.Header" xml:space="preserve"> <data name="DeleteTheme.Header" xml:space="preserve">
<value>Delete Theme</value> <value>Delete Theme</value>
</data> </data>
<data name="DeleteTheme.Text" xml:space="preserve">
<value>Delete</value>
</data>
<data name="CreateTheme.Text" xml:space="preserve"> <data name="CreateTheme.Text" xml:space="preserve">
<value>Create Theme</value> <value>Create Theme</value>
</data> </data>
@ -147,6 +150,9 @@
<data name="ViewTheme.Text" xml:space="preserve"> <data name="ViewTheme.Text" xml:space="preserve">
<value>View</value> <value>View</value>
</data> </data>
<data name="EditTheme.Text" xml:space="preserve">
<value>Edit</value>
</data>
<data name="Enabled" xml:space="preserve"> <data name="Enabled" xml:space="preserve">
<value>Enabled?</value> <value>Enabled?</value>
</data> </data>

View File

@ -144,7 +144,10 @@
<data name="Message.Text" xml:space="preserve"> <data name="Message.Text" xml:space="preserve">
<value>Framework Is Already Up To Date</value> <value>Framework Is Already Up To Date</value>
</data> </data>
<data name="MessgeUpgrade.Text" xml:space="preserve"> <data name="MessageUpgrade.Text" xml:space="preserve">
<value>Upload A Framework Package (Oqtane.Framework.version.nupkg) And Then Select Upgrade</value> <value>Upload A Framework Package (Oqtane.Framework.version.nupkg) And Then Select Upgrade</value>
</data> </data>
<data name="Localhost.Text" xml:space="preserve">
<value>You Cannot Perform A System Update In A Development Environment</value>
</data>
</root> </root>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<root> <root>
<!-- <!--
Microsoft ResX Schema Microsoft ResX Schema
@ -141,7 +141,7 @@
<data name="Subject.Text" xml:space="preserve"> <data name="Subject.Text" xml:space="preserve">
<value>Subject: </value> <value>Subject: </value>
</data> </data>
<data name="ModuleTitle.Text" xml:space="preserve"> <data name="Send Notification" xml:space="preserve">
<value>Send Notification</value> <value>Send Notification</value>
</data> </data>
</root> </root>

View File

@ -228,4 +228,10 @@
<data name="Profile.Heading" xml:space="preserve"> <data name="Profile.Heading" xml:space="preserve">
<value>Profile</value> <value>Profile</value>
</data> </data>
<data name="ViewNotification.Text" xml:space="preserve">
<value>View</value>
</data>
<data name="DeleteNotification.Text" xml:space="preserve">
<value>Delete</value>
</data>
</root> </root>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<root> <root>
<!-- <!--
Microsoft ResX Schema Microsoft ResX Schema
@ -144,7 +144,7 @@
<data name="OriginalMessage" xml:space="preserve"> <data name="OriginalMessage" xml:space="preserve">
<value>Original Message</value> <value>Original Message</value>
</data> </data>
<data name="ModuleTitle.Text" xml:space="preserve"> <data name="View Notification" xml:space="preserve">
<value>View Notification</value> <value>View Notification</value>
</data> </data>
</root> </root>

View File

@ -118,7 +118,7 @@
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader> </resheader>
<data name="Error.User.AddCheckPass" xml:space="preserve"> <data name="Error.User.AddCheckPass" xml:space="preserve">
<value>Error Adding User. Please Ensure Password Meets Complexity Requirements And Username Is Not Already In Use.</value> <value>Error Adding User. Please Ensure Password Meets Complexity Requirements And Username And Email Is Not Already In Use.</value>
</data> </data>
<data name="Message.Password.NoMatch" xml:space="preserve"> <data name="Message.Password.NoMatch" xml:space="preserve">
<value>Passwords Entered Do Not Match</value> <value>Passwords Entered Do Not Match</value>
@ -171,4 +171,10 @@
<data name="Password.Placeholder" xml:space="preserve"> <data name="Password.Placeholder" xml:space="preserve">
<value>Password</value> <value>Password</value>
</data> </data>
<data name="Notify.HelpText" xml:space="preserve">
<value>Indicate if new users should receive an email notification</value>
</data>
<data name="Notify.Text" xml:space="preserve">
<value>Notify?</value>
</data>
</root> </root>

View File

@ -396,4 +396,16 @@
<data name="ProfileClaimTypes.Text" xml:space="preserve"> <data name="ProfileClaimTypes.Text" xml:space="preserve">
<value>User Profile Claims:</value> <value>User Profile Claims:</value>
</data> </data>
<data name="Username" xml:space="preserve">
<value>User Name</value>
</data>
<data name="Name" xml:space="preserve">
<value>Name</value>
</data>
<data name="Email" xml:space="preserve">
<value>Email</value>
</data>
<data name="ImportUsers.Text" xml:space="preserve">
<value>Import Users</value>
</data>
</root> </root>

View File

@ -117,61 +117,34 @@
<resheader name="writer"> <resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader> </resheader>
<data name="Template.Text" xml:space="preserve"> <data name="ImportFile.HelpText" xml:space="preserve">
<value>Template: </value> <value>Upload or select a tab delimited text file containing user information. The file must be in the Template format specified (Roles can be specified as a comma delimited list).</value>
</data> </data>
<data name="Template.Select" xml:space="preserve"> <data name="ImportFile.Text" xml:space="preserve">
<value>Select Template</value> <value>Import File:</value>
</data> </data>
<data name="Module.Create" xml:space="preserve"> <data name="Error.Import" xml:space="preserve">
<value>Create Module</value> <value>Error Importing Users</value>
</data> </data>
<data name="Module.Activate" xml:space="preserve"> <data name="Import" xml:space="preserve">
<value>Activate Module</value> <value>Import</value>
</data> </data>
<data name="Info.Module.Creator" xml:space="preserve"> <data name="Message.Import.Failure" xml:space="preserve">
<value>Please Note That The Module Creator Is Only Intended To Be Used In A Development Environment</value> <value>User Import Failed. Please Review Your Event Log For More Detailed Information.</value>
</data> </data>
<data name="Info.Module.Activate" xml:space="preserve"> <data name="Message.Import.Success" xml:space="preserve">
<value>Once You Have Compiled The Module And Restarted The Application You Can Activate The Module Below</value> <value>User Import Successful. {0} Users Imported.</value>
</data> </data>
<data name="Success.Module.Create" xml:space="preserve"> <data name="Message.Import.Validation" xml:space="preserve">
<value>The Source Code For Your Module Has Been Created At The Location Specified Below And Must Be Compiled In Order To Make It Functional. Once It Has Been Compiled You Must &lt;a href={0}&gt;Restart&lt;/a&gt; Your Application To Apply These Changes.</value> <value>You Must Specify A User File For Import</value>
</data> </data>
<data name="Message.Require.ValidName" xml:space="preserve"> <data name="Template" xml:space="preserve">
<value>You Must Provide A Valid Owner Name And Module Name ( ie. No Punctuation Or Spaces And The Values Cannot Be The Same ) And Choose A Template</value> <value>Template</value>
</data> </data>
<data name="OwnerName.HelpText" xml:space="preserve"> <data name="Notify.HelpText" xml:space="preserve">
<value>Enter the name of the organization who is developing this module. It should not contain spaces or punctuation.</value> <value>Indicate if new users should receive an email notification</value>
</data> </data>
<data name="ModuleName.HelpText" xml:space="preserve"> <data name="Notify.Text" xml:space="preserve">
<value>Enter a name for this module. It should not contain spaces or punctuation.</value> <value>Notify?</value>
</data>
<data name="Description.HelpText" xml:space="preserve">
<value>Enter a short description for the module</value>
</data>
<data name="Template.HelpText" xml:space="preserve">
<value>Select a module template. Templates are located in the wwwroot/Modules/Templates folder on the server.</value>
</data>
<data name="FrameworkReference.HelpText" xml:space="preserve">
<value>Select a framework reference version</value>
</data>
<data name="Location.HelpText" xml:space="preserve">
<value>Location where the module will be created</value>
</data>
<data name="OwnerName.Text" xml:space="preserve">
<value>Owner Name: </value>
</data>
<data name="ModuleName.Text" xml:space="preserve">
<value>Module Name: </value>
</data>
<data name="Description.Text" xml:space="preserve">
<value>Description: </value>
</data>
<data name="FrameworkReference.Text" xml:space="preserve">
<value>Framework Reference: </value>
</data>
<data name="Location.Text" xml:space="preserve">
<value>Location: </value>
</data> </data>
</root> </root>

View File

@ -129,4 +129,25 @@
<data name="Message.Username.DontExist" xml:space="preserve"> <data name="Message.Username.DontExist" xml:space="preserve">
<value>User Does Not Exist With Name Specified</value> <value>User Does Not Exist With Name Specified</value>
</data> </data>
<data name="ModuleDefinition" xml:space="preserve">
<value>Module</value>
</data>
<data name="Page" xml:space="preserve">
<value>Page</value>
</data>
<data name="Folder" xml:space="preserve">
<value>Folder</value>
</data>
<data name="View" xml:space="preserve">
<value>View</value>
</data>
<data name="Edit" xml:space="preserve">
<value>Edit</value>
</data>
<data name="Utilize" xml:space="preserve">
<value>Utilize</value>
</data>
<data name="Browse" xml:space="preserve">
<value>Browse</value>
</data>
</root> </root>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<root> <root>
<!-- <!--
Microsoft ResX Schema Microsoft ResX Schema
@ -156,7 +156,7 @@
<data name="Message.Content.Restored" xml:space="preserve"> <data name="Message.Content.Restored" xml:space="preserve">
<value>Version Restored</value> <value>Version Restored</value>
</data> </data>
<data name="ModuleTitle.Text" xml:space="preserve"> <data name="Edit Html/Text" xml:space="preserve">
<value>Edit Html/Text</value> <value>Edit Html/Text</value>
</data> </data>
<data name="Restore.Header" xml:space="preserve"> <data name="Restore.Header" xml:space="preserve">
@ -168,4 +168,16 @@
<data name="View.Text" xml:space="preserve"> <data name="View.Text" xml:space="preserve">
<value>View</value> <value>View</value>
</data> </data>
<data name="Edit.Heading" xml:space="preserve">
<value>Edit</value>
</data>
<data name="Versions.Heading" xml:space="preserve">
<value>Versions</value>
</data>
<data name="HtmlEditor.Heading" xml:space="preserve">
<value>Raw HTML Editor</value>
</data>
<data name="RichTextEditor.Heading" xml:space="preserve">
<value>Rich Text Editor</value>
</data>
</root> </root>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<root> <root>
<!-- <!--
Microsoft ResX Schema Microsoft ResX Schema
@ -120,6 +120,9 @@
<data name="Edit.Action" xml:space="preserve"> <data name="Edit.Action" xml:space="preserve">
<value>Edit</value> <value>Edit</value>
</data> </data>
<data name="Edit.Text" xml:space="preserve">
<value>Edit</value>
</data>
<data name="Error.Content.Load" xml:space="preserve"> <data name="Error.Content.Load" xml:space="preserve">
<value>An Error Occurred Loading Content</value> <value>An Error Occurred Loading Content</value>
</data> </data>

View File

@ -405,4 +405,25 @@
<data name="Search.RecentlyReleased" xml:space="preserve"> <data name="Search.RecentlyReleased" xml:space="preserve">
<value>Recently Released</value> <value>Recently Released</value>
</data> </data>
<data name="From" xml:space="preserve">
<value>From</value>
</data>
<data name="To" xml:space="preserve">
<value>To</value>
</data>
<data name="Password.DigitRequirement" xml:space="preserve">
<value>At Least One Digit</value>
</data>
<data name="Password.LowercaseRequirement" xml:space="preserve">
<value>At Least One Lowercase Letter</value>
</data>
<data name="Password.PunctuationRequirement" xml:space="preserve">
<value>At Least One Punctuation Mark</value>
</data>
<data name="Password.UppercaseRequirement" xml:space="preserve">
<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>
</data>
</root> </root>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<root> <root>
<!-- <!--
Microsoft ResX Schema Microsoft ResX Schema
@ -186,4 +186,7 @@
<data name="VisibilityView" xml:space="preserve"> <data name="VisibilityView" xml:space="preserve">
<value>Same As Page</value> <value>Same As Page</value>
</data> </data>
<data name="Confirm.Page.Delete" xml:space="preserve">
<value>Are You Sure You Want To Delete This Page?</value>
</data>
</root> </root>

View File

@ -29,7 +29,7 @@ namespace Oqtane.Services
public async Task<Folder> GetFolderAsync(int siteId, [NotNull] string folderPath) public async Task<Folder> GetFolderAsync(int siteId, [NotNull] string folderPath)
{ {
var path = WebUtility.UrlEncode(folderPath); var path = WebUtility.UrlEncode(folderPath);
return await GetJsonAsync<Folder>($"{ApiUrl}/{siteId}/{path}"); return await GetJsonAsync<Folder>($"{ApiUrl}/path/{siteId}/?path={path}");
} }
public async Task<Folder> AddFolderAsync(Folder folder) public async Task<Folder> AddFolderAsync(Folder folder)

View File

@ -38,13 +38,19 @@ namespace Oqtane.Services
/// <returns></returns> /// <returns></returns>
Task<List<Package>> GetPackagesAsync(string type, string search, string price, string package, string sort); Task<List<Package>> GetPackagesAsync(string type, string search, string price, string package, string sort);
/// <summary>
/// Returns a list of packages based on installationid
/// </summary>
/// <returns></returns>
Task<List<Package>> GetPackageUpdatesAsync(string type);
/// <summary> /// <summary>
/// Returns a specific package /// Returns a specific package
/// </summary> /// </summary>
/// <param name="packageId"></param> /// <param name="packageId"></param>
/// <param name="version"></param> /// <param name="version"></param>
/// <returns></returns> /// <returns></returns>
Task<Package> GetPackageAsync(string packageId, string version); Task<Package> GetPackageAsync(string packageId, string version, bool download);
/// <summary> /// <summary>
/// Downloads a specific package as .nupkg file /// Downloads a specific package as .nupkg file
@ -53,7 +59,7 @@ namespace Oqtane.Services
/// <param name="version"></param> /// <param name="version"></param>
/// <param name="folder"></param> /// <param name="folder"></param>
/// <returns></returns> /// <returns></returns>
Task DownloadPackageAsync(string packageId, string version, string folder); Task DownloadPackageAsync(string packageId, string version);
/// <summary> /// <summary>
/// Installs all packages located in //TODO: 2dm where? /// Installs all packages located in //TODO: 2dm where?

View File

@ -9,13 +9,13 @@ namespace Oqtane.Services
public interface ISystemService public interface ISystemService
{ {
/// <summary> /// <summary>
/// returns a key-value directory with the current system configuration information /// returns a key-value dictionary with the current system configuration information
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
Task<Dictionary<string, object>> GetSystemInfoAsync(); Task<Dictionary<string, object>> GetSystemInfoAsync();
/// <summary> /// <summary>
/// returns a key-value directory with the current system information - "environment" or "configuration" /// returns a key-value dictionary with the current system information - "environment" or "configuration"
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
Task<Dictionary<string, object>> GetSystemInfoAsync(string type); Task<Dictionary<string, object>> GetSystemInfoAsync(string type);
@ -32,5 +32,11 @@ namespace Oqtane.Services
/// <param name="settings"></param> /// <param name="settings"></param>
/// <returns></returns> /// <returns></returns>
Task UpdateSystemInfoAsync(Dictionary<string, object> settings); Task UpdateSystemInfoAsync(Dictionary<string, object> settings);
/// <summary>
/// returns a key-value dictionary with default system icons
/// </summary>
/// <returns></returns>
Task<Dictionary<string, string>> GetIconsAsync();
} }
} }

View File

@ -1,4 +1,5 @@
using Oqtane.Models; using Oqtane.Models;
using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Oqtane.Services namespace Oqtane.Services
@ -16,7 +17,6 @@ namespace Oqtane.Services
/// <returns></returns> /// <returns></returns>
Task<User> GetUserAsync(int userId, int siteId); Task<User> GetUserAsync(int userId, int siteId);
/// <summary> /// <summary>
/// Get a <see cref="User"/> of a specific site /// Get a <see cref="User"/> of a specific site
/// </summary> /// </summary>
@ -25,6 +25,15 @@ namespace Oqtane.Services
/// <returns></returns> /// <returns></returns>
Task<User> GetUserAsync(string username, int siteId); Task<User> GetUserAsync(string username, int siteId);
/// <summary>
/// Get a <see cref="User"/> of a specific site
/// </summary>
/// <param name="username">Username / login of a <see cref="User"/></param>
/// <param name="email">email address of a <see cref="User"/></param>
/// <param name="siteId">ID of a <see cref="Site"/></param>
/// <returns></returns>
Task<User> GetUserAsync(string username, string email, int siteId);
/// <summary> /// <summary>
/// Save a user to the Database. /// Save a user to the Database.
/// The <see cref="User"/> object contains all the information incl. what <see cref="Site"/> it belongs to. /// The <see cref="User"/> object contains all the information incl. what <see cref="Site"/> it belongs to.
@ -127,6 +136,20 @@ namespace Oqtane.Services
/// <returns></returns> /// <returns></returns>
Task<User> LinkUserAsync(User user, string token, string type, string key, string name); Task<User> LinkUserAsync(User user, string token, string type, string key, string name);
/// <summary>
/// Get password requirements for site
/// </summary>
/// <param name="siteId">ID of a <see cref="Site"/></param>
/// <returns></returns>
Task<string> GetPasswordRequirementsAsync(int siteId);
/// <summary>
/// Bulk import of users
/// </summary>
/// <param name="siteId">ID of a <see cref="Site"/></param>
/// <param name="fileId">ID of a <see cref="File"/></param>
/// <param name="notify">Indicates if new users should be notified by email</param>
/// <returns></returns>
Task<Dictionary<string, string>> ImportUsersAsync(int siteId, int fileId, bool notify);
} }
} }

View File

@ -31,14 +31,19 @@ namespace Oqtane.Services
return await GetJsonAsync<List<Package>>($"{Apiurl}?type={type}&search={WebUtility.UrlEncode(search)}&price={price}&package={package}&sort={sort}"); return await GetJsonAsync<List<Package>>($"{Apiurl}?type={type}&search={WebUtility.UrlEncode(search)}&price={price}&package={package}&sort={sort}");
} }
public async Task<Package> GetPackageAsync(string packageId, string version) public async Task<List<Package>> GetPackageUpdatesAsync(string type)
{ {
return await PostJsonAsync<Package>($"{Apiurl}?packageid={packageId}&version={version}", null); return await GetJsonAsync<List<Package>>($"{Apiurl}/updates/?type={type}");
} }
public async Task DownloadPackageAsync(string packageId, string version, string folder) public async Task<Package> GetPackageAsync(string packageId, string version, bool download)
{ {
await PostAsync($"{Apiurl}?packageid={packageId}&version={version}&folder={folder}"); return await PostJsonAsync<Package>($"{Apiurl}?packageid={packageId}&version={version}&download={download}&install=false", null);
}
public async Task DownloadPackageAsync(string packageId, string version)
{
await PostAsync($"{Apiurl}?packageid={packageId}&version={version}&download=true&install=true");
} }
public async Task InstallPackagesAsync() public async Task InstallPackagesAsync()

View File

@ -33,5 +33,10 @@ namespace Oqtane.Services
{ {
await PostJsonAsync(Apiurl, settings); await PostJsonAsync(Apiurl, settings);
} }
public async Task<Dictionary<string, string>> GetIconsAsync()
{
return await GetJsonAsync<Dictionary<string, string>>($"{Apiurl}/icons");
}
} }
} }

View File

@ -4,13 +4,23 @@ using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using Oqtane.Documentation; using Oqtane.Documentation;
using System.Net; using System.Net;
using System.Collections.Generic;
using Microsoft.Extensions.Localization;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Oqtane.Modules.Admin.Roles;
using System.Xml.Linq;
namespace Oqtane.Services namespace Oqtane.Services
{ {
[PrivateApi("Don't show in the documentation, as everything should use the Interface")] [PrivateApi("Don't show in the documentation, as everything should use the Interface")]
public class UserService : ServiceBase, IUserService public class UserService : ServiceBase, IUserService
{ {
public UserService(HttpClient http, SiteState siteState) : base(http, siteState) { } private readonly IStringLocalizer<SharedResources> _localizer;
public UserService(IStringLocalizer<SharedResources> localizer, HttpClient http, SiteState siteState) : base(http, siteState)
{
_localizer = localizer;
}
private string Apiurl => CreateApiUrl("User"); private string Apiurl => CreateApiUrl("User");
@ -21,7 +31,12 @@ namespace Oqtane.Services
public async Task<User> GetUserAsync(string username, int siteId) public async Task<User> GetUserAsync(string username, int siteId)
{ {
return await GetJsonAsync<User>($"{Apiurl}/name/{username}?siteid={siteId}"); return await GetUserAsync(username, "", siteId);
}
public async Task<User> GetUserAsync(string username, string email, int siteId)
{
return await GetJsonAsync<User>($"{Apiurl}/name/{(!string.IsNullOrEmpty(username) ? username : "-")}/{(!string.IsNullOrEmpty(email) ? email : "-")}/?siteid={siteId}");
} }
public async Task<User> AddUserAsync(User user) public async Task<User> AddUserAsync(User user)
@ -90,5 +105,31 @@ namespace Oqtane.Services
return await PostJsonAsync<User>($"{Apiurl}/link?token={token}&type={type}&key={key}&name={name}", user); return await PostJsonAsync<User>($"{Apiurl}/link?token={token}&type={type}&key={key}&name={name}", user);
} }
public async Task<string> GetPasswordRequirementsAsync(int siteId)
{
var requirements = await GetJsonAsync<Dictionary<string, string>>($"{Apiurl}/passwordrequirements/{siteId}");
var minimumlength = (requirements.ContainsKey("IdentityOptions:Password:RequiredLength")) ? requirements["IdentityOptions:Password:RequiredLength"] : "6";
var uniquecharacters = (requirements.ContainsKey("IdentityOptions:Password:RequiredUniqueChars")) ? requirements["IdentityOptions:Password:RequiredUniqueChars"] : "1";
var requiredigit = bool.Parse((requirements.ContainsKey("IdentityOptions:Password:RequireDigit")) ? requirements["IdentityOptions:Password:RequireDigit"] : "true");
var requireupper = bool.Parse((requirements.ContainsKey("IdentityOptions:Password:RequireUppercase")) ? requirements["IdentityOptions:Password:RequireUppercase"] : "true");
var requirelower = bool.Parse((requirements.ContainsKey("IdentityOptions:Password:RequireLowercase")) ? requirements["IdentityOptions:Password:RequireLowercase"] : "true");
var requirepunctuation = bool.Parse((requirements.ContainsKey("IdentityOptions:Password:RequireNonAlphanumeric")) ? requirements["IdentityOptions:Password:RequireNonAlphanumeric"] : "true");
// replace the placeholders with the setting values
string digitRequirement = requiredigit ? _localizer["Password.DigitRequirement"] + ", " : "";
string uppercaseRequirement = requireupper ? _localizer["Password.UppercaseRequirement"] + ", " : "";
string lowercaseRequirement = requirelower ? _localizer["Password.LowercaseRequirement"] + ", " : "";
string punctuationRequirement = requirepunctuation ? _localizer["Password.PunctuationRequirement"] + ", " : "";
string passwordValidationCriteriaTemplate = _localizer["Password.ValidationCriteria"];
// format requirements
return string.Format(passwordValidationCriteriaTemplate, minimumlength, uniquecharacters, digitRequirement, uppercaseRequirement, lowercaseRequirement, punctuationRequirement);
}
public async Task<Dictionary<string, string>> ImportUsersAsync(int siteId, int fileId, bool notify)
{
return await PostJsonAsync<Dictionary<string, string>>($"{Apiurl}/import?siteid={siteId}&fileid={fileId}&notify={notify}", null);
}
} }
} }

View File

@ -3,6 +3,7 @@
@inherits ContainerBase @inherits ContainerBase
@attribute [OqtaneIgnore] @attribute [OqtaneIgnore]
@inject IStringLocalizer<SharedResources> SharedLocalizer @inject IStringLocalizer<SharedResources> SharedLocalizer
@inject IStringLocalizerFactory LocalizerFactory
<span class="app-moduletitle"> <span class="app-moduletitle">
@((MarkupString)title) @((MarkupString)title)
@ -20,7 +21,8 @@
{ {
if (!string.IsNullOrEmpty(ModuleState.ControlTitle)) if (!string.IsNullOrEmpty(ModuleState.ControlTitle))
{ {
title = ModuleState.ControlTitle; var localizer = LocalizerFactory.Create(ModuleState.ModuleType);
title = localizer[ModuleState.ControlTitle];
} }
else else
{ {

View File

@ -10,6 +10,8 @@
@inject IPageModuleService PageModuleService @inject IPageModuleService PageModuleService
@inject ILogService logger @inject ILogService logger
@inject ISettingService SettingService @inject ISettingService SettingService
@inject IJSRuntime jsRuntime
@inject IServiceProvider ServiceProvider
@inject IStringLocalizer<ControlPanel> Localizer @inject IStringLocalizer<ControlPanel> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer @inject IStringLocalizer<SharedResources> SharedLocalizer
@ -36,14 +38,14 @@
@if (_canViewAdminDashboard || UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.PermissionList)) @if (_canViewAdminDashboard || UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.PermissionList))
{ {
<button type="button" class="btn @ButtonClass ms-1" data-bs-toggle="offcanvas" data-bs-target="#offcanvasControlPanel" aria-controls="offcanvasControlPanel"> <button type="button" class="btn @ButtonClass ms-1" data-bs-toggle="offcanvas" data-bs-target="#offcanvasControlPanel" aria-controls="offcanvasControlPanel" @onclick="ClearMessage">
<span class="oi oi-cog"></span> <span class="oi oi-cog"></span>
</button> </button>
<div class="@ContainerClass" tabindex="-1" data-bs-scroll="true" data-bs-backdrop="true" id="offcanvasControlPanel" aria-labelledby="offcanvasScrollingLabel"> <div class="@ContainerClass" tabindex="-1" data-bs-scroll="true" data-bs-backdrop="true" id="offcanvasControlPanel" aria-labelledby="offcanvasScrollingLabel">
<div class="@HeaderClass"> <div class="@HeaderClass">
<h5 id="offcanvasScrollingLabel" class="offcanvas-title">@Localizer["ControlPanel"]</h5> <h5 id="offcanvasScrollingLabel" class="offcanvas-title">@Localizer["ControlPanel"]</h5>
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" aria-label="Close"></button> <button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" aria-label="Close" @onclick="ClearMessage"></button>
</div> </div>
<div class="@BodyClass"> <div class="@BodyClass">
<div class="container-fluid"> <div class="container-fluid">
@ -98,7 +100,7 @@
<button type="button" class="btn-close" aria-label="Close" @onclick="ConfirmDelete"></button> <button type="button" class="btn-close" aria-label="Close" @onclick="ConfirmDelete"></button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<p>Are You Sure You Want To Delete This Page?</p> <p>@Localizer["Confirm.Page.Delete"]</p>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-danger" @onclick="DeletePage">@SharedLocalizer["Delete"]</button> <button type="button" class="btn btn-danger" @onclick="DeletePage">@SharedLocalizer["Delete"]</button>
@ -109,10 +111,7 @@
</div> </div>
</div> </div>
} }
}
@if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.PermissionList))
{
<div class="row"> <div class="row">
<div class="col text-center"> <div class="col text-center">
<label for="Module" class="control-label">@Localizer["Module.Manage"] </label> <label for="Module" class="control-label">@Localizer["Module.Manage"] </label>
@ -220,7 +219,14 @@
</div> </div>
<button type="button" class="btn btn-primary col-12 mt-4" @onclick="@AddModule">@Localizer["Page.Module.Add"]</button> <button type="button" class="btn btn-primary col-12 mt-4" @onclick="@AddModule">@Localizer["Page.Module.Add"]</button>
@((MarkupString)Message) @((MarkupString)Message)
<hr class="app-rule" />
} }
<div class="row d-flex">
<div class="col">
<button type="button" data-bs-dismiss="offcanvas" class="btn btn-secondary col-12" @onclick=@(async () => await LogoutUser())>@Localizer["Logout"]</button>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -243,6 +249,15 @@
protected string ModuleType { get; private set; } = "new"; protected string ModuleType { get; private set; } = "new";
protected string ModuleDefinitionName { get; private set; } = "-"; protected string ModuleDefinitionName { get; private set; } = "-";
protected string Title { get; private set; } = "";
protected string ContainerType { get; private set; } = "";
protected string Visibility { get; private set; } = "view";
protected string Message { get; private set; } = "";
private string settingCategory = "CP-category";
private string settingPane = "CP-pane";
private string _pane = "";
protected string Category protected string Category
{ {
get => _category; get => _category;
@ -273,11 +288,6 @@
} }
} }
protected string Title { get; private set; } = "";
protected string ContainerType { get; private set; } = "";
protected string Visibility { get; private set; } = "view";
protected string Message { get; private set; } = "";
[Parameter] [Parameter]
public string ButtonClass { get; set; } = "btn-outline-secondary"; public string ButtonClass { get; set; } = "btn-outline-secondary";
@ -293,7 +303,6 @@
[Parameter] [Parameter]
public bool ShowLanguageSwitcher { get; set; } = true; public bool ShowLanguageSwitcher { get; set; } = true;
protected override async Task OnParametersSetAsync() protected override async Task OnParametersSetAsync()
{ {
_canViewAdminDashboard = CanViewAdminDashboard(); _canViewAdminDashboard = CanViewAdminDashboard();
@ -613,9 +622,36 @@
} }
} }
private string settingCategory = "CP-category"; // the following code is duplicated from LoginBase
private string settingPane = "CP-pane"; private async Task LogoutUser()
private string _pane = ""; {
await LoggingService.Log(PageState.Alias, PageState.Page.PageId, null, PageState.User?.UserId, GetType().AssemblyQualifiedName, "Logout", LogFunction.Security, LogLevel.Information, null, "User Logout For Username {Username}", PageState.User?.Username);
Route route = new Route(PageState.Uri.AbsoluteUri, PageState.Alias.Path);
var url = route.PathAndQuery;
// verify if anonymous users can access page
if (!UserSecurity.IsAuthorized(null, PermissionNames.View, PageState.Page.PermissionList))
{
url = PageState.Alias.Path;
}
if (PageState.Runtime == Shared.Runtime.Hybrid)
{
// hybrid apps utilize an interactive logout
await UserService.LogoutUserAsync(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 };
var interop = new Interop(jsRuntime);
await interop.SubmitForm(Utilities.TenantUrl(PageState.Alias, "/pages/logout/"), fields);
}
}
private async Task LoadSettingsAsync() private async Task LoadSettingsAsync()
{ {
@ -647,4 +683,10 @@
await SettingService.UpdateUserSettingsAsync(settings, PageState.User.UserId); await SettingService.UpdateUserSettingsAsync(settings, PageState.User.UserId);
} }
private void ClearMessage()
{
Message = "";
}
} }

View File

@ -8,7 +8,7 @@
@if (_supportedCultures?.Count() > 1) @if (_supportedCultures?.Count() > 1)
{ {
<div class="btn-group" role="group"> <div class="btn-group pe-1" role="group">
<button id="btnCultures" type="button" class="btn btn-outline-secondary dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> <button id="btnCultures" type="button" class="btn btn-outline-secondary dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="oi oi-globe"></span> <span class="oi oi-globe"></span>
</button> </button>

View File

@ -17,6 +17,7 @@ namespace Oqtane.Themes.Controls
{ {
[Inject] public NavigationManager NavigationManager { get; set; } [Inject] public NavigationManager NavigationManager { get; set; }
[Inject] public IUserService UserService { get; set; } [Inject] public IUserService UserService { get; set; }
[Inject] public ISettingService SettingService { get; set; }
[Inject] public IJSRuntime jsRuntime { get; set; } [Inject] public IJSRuntime jsRuntime { get; set; }
[Inject] public IServiceProvider ServiceProvider { get; set; } [Inject] public IServiceProvider ServiceProvider { get; set; }

View File

@ -28,11 +28,11 @@ namespace Oqtane.UI
public List<Page> Pages public List<Page> Pages
{ {
get { return Site.Pages.Where(item => !item.IsDeleted).ToList(); } get { return Site.Pages; }
} }
public List<Module> Modules public List<Module> Modules
{ {
get { return Site.Modules.Where(item => !item.IsDeleted).ToList(); } get { return Site.Modules; }
} }
public List<Language> Languages public List<Language> Languages
{ {

View File

@ -146,6 +146,10 @@
{ {
editmode = false; // reset edit mode when navigating to different page editmode = false; // reset edit mode when navigating to different page
} }
if (querystring.ContainsKey("edit") && querystring["edit"] == "true")
{
editmode = true; // querystring can set edit mode
}
// get user // get user
if (PageState == null || refresh || PageState.Alias.SiteId != SiteState.Alias.SiteId) if (PageState == null || refresh || PageState.Alias.SiteId != SiteState.Alias.SiteId)
@ -290,7 +294,7 @@
var urlMapping = await UrlMappingService.GetUrlMappingAsync(site.SiteId, route.PagePath); var urlMapping = await UrlMappingService.GetUrlMappingAsync(site.SiteId, route.PagePath);
if (urlMapping != null && !string.IsNullOrEmpty(urlMapping.MappedUrl)) if (urlMapping != null && !string.IsNullOrEmpty(urlMapping.MappedUrl))
{ {
var url = (urlMapping.MappedUrl.StartsWith("http")) ? urlMapping.MappedUrl : route.SiteUrl + "/" + urlMapping.MappedUrl; var url = (urlMapping.MappedUrl.StartsWith("http")) ? urlMapping.MappedUrl : route.SiteUrl + "/" + urlMapping.MappedUrl + route.Query;
NavigationManager.NavigateTo(url, false); NavigationManager.NavigateTo(url, false);
} }
else // not mapped else // not mapped
@ -387,6 +391,7 @@
private (Page Page, List<Module> Modules) ProcessModules(Page page, List<Module> modules, int moduleid, string action, string defaultcontainertype, Alias alias) private (Page Page, List<Module> Modules) ProcessModules(Page page, List<Module> modules, int moduleid, string action, string defaultcontainertype, Alias alias)
{ {
var paneindex = new Dictionary<string, int>(); var paneindex = new Dictionary<string, int>();
foreach (Module module in modules) foreach (Module module in modules)
{ {
// initialize module control properties // initialize module control properties
@ -397,7 +402,7 @@
module.PaneModuleIndex = -1; module.PaneModuleIndex = -1;
module.PaneModuleCount = 0; module.PaneModuleCount = 0;
if ((module.PageId == page.PageId || module.ModuleId == moduleid)) if (module.PageId == page.PageId || module.ModuleId == moduleid)
{ {
var typename = Constants.ErrorModule; var typename = Constants.ErrorModule;
@ -463,8 +468,8 @@
// additional metadata needed for admin components // additional metadata needed for admin components
if (module.ModuleId == moduleid && action != "") if (module.ModuleId == moduleid && action != "")
{ {
module.SecurityAccessLevel = moduleobject.SecurityAccessLevel;
module.ControlTitle = moduleobject.Title; module.ControlTitle = moduleobject.Title;
module.SecurityAccessLevel = moduleobject.SecurityAccessLevel;
module.Actions = moduleobject.Actions; module.Actions = moduleobject.Actions;
module.UseAdminContainer = moduleobject.UseAdminContainer; module.UseAdminContainer = moduleobject.UseAdminContainer;
} }

View File

@ -67,16 +67,19 @@
if (!string.IsNullOrEmpty(content)) if (!string.IsNullOrEmpty(content))
{ {
// format head content, remove scripts, and filter duplicate elements // format head content, remove scripts, and filter duplicate elements
var elements = (">" + content.Replace("\n", "") + "<").Split("><"); content = content.Replace("\n", "");
foreach (var element in elements) var index = content.IndexOf("<");
while (index >= 0)
{ {
if (!string.IsNullOrEmpty(element) && !element.ToLower().StartsWith("script")) var element = content.Substring(index, content.IndexOf(">", index) - index + 1);
if (!string.IsNullOrEmpty(element) && !element.ToLower().StartsWith("<script") && !element.ToLower().StartsWith("</script"))
{ {
if (!headcontent.Contains("<" + element + ">")) if (!headcontent.Contains(element))
{ {
headcontent += "<" + element + ">" + "\n"; headcontent += element + "\n";
} }
} }
index = content.IndexOf("<", index + 1);
} }
} }
return headcontent; return headcontent;
@ -163,7 +166,7 @@
if (!string.IsNullOrEmpty(src)) if (!string.IsNullOrEmpty(src))
{ {
src = (src.Contains("://")) ? src : PageState.Alias.BaseUrl + src; src = (src.Contains("://")) ? src : PageState.Alias.BaseUrl + src;
scripts.Add(new { href = src, bundle = "", integrity = integrity, crossorigin = crossorigin, es6module = (type == "module"), location = location }); scripts.Add(new { href = src, bundle = "", integrity = integrity, crossorigin = crossorigin, es6module = (type == "module"), location = location.ToString().ToLower() });
} }
else else
{ {

View File

@ -2,7 +2,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>net7.0</TargetFramework> <TargetFramework>net7.0</TargetFramework>
<Version>4.0.2</Version> <Version>4.0.4</Version>
<Product>Oqtane</Product> <Product>Oqtane</Product>
<Authors>Shaun Walker</Authors> <Authors>Shaun Walker</Authors>
<Company>.NET Foundation</Company> <Company>.NET Foundation</Company>
@ -10,7 +10,7 @@
<Copyright>.NET Foundation</Copyright> <Copyright>.NET Foundation</Copyright>
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl> <PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl> <PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.2</PackageReleaseNotes> <PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.4</PackageReleaseNotes>
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl> <RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
<RepositoryType>Git</RepositoryType> <RepositoryType>Git</RepositoryType>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>

View File

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd"> <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata> <metadata>
<id>Oqtane.Database.MySQL</id> <id>Oqtane.Database.MySQL</id>
<version>4.0.2</version> <version>4.0.4</version>
<authors>Shaun Walker</authors> <authors>Shaun Walker</authors>
<owners>.NET Foundation</owners> <owners>.NET Foundation</owners>
<title>Oqtane MySQL Provider</title> <title>Oqtane MySQL Provider</title>
@ -12,7 +12,7 @@
<requireLicenseAcceptance>false</requireLicenseAcceptance> <requireLicenseAcceptance>false</requireLicenseAcceptance>
<license type="expression">MIT</license> <license type="expression">MIT</license>
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl> <projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.2</releaseNotes> <releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.4</releaseNotes>
<icon>icon.png</icon> <icon>icon.png</icon>
<tags>oqtane</tags> <tags>oqtane</tags>
</metadata> </metadata>

View File

@ -2,7 +2,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>net7.0</TargetFramework> <TargetFramework>net7.0</TargetFramework>
<Version>4.0.2</Version> <Version>4.0.4</Version>
<Product>Oqtane</Product> <Product>Oqtane</Product>
<Authors>Shaun Walker</Authors> <Authors>Shaun Walker</Authors>
<Company>.NET Foundation</Company> <Company>.NET Foundation</Company>
@ -10,7 +10,7 @@
<Copyright>.NET Foundation</Copyright> <Copyright>.NET Foundation</Copyright>
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl> <PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl> <PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.2</PackageReleaseNotes> <PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.4</PackageReleaseNotes>
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl> <RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
<RepositoryType>Git</RepositoryType> <RepositoryType>Git</RepositoryType>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>

View File

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd"> <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata> <metadata>
<id>Oqtane.Database.PostgreSQL</id> <id>Oqtane.Database.PostgreSQL</id>
<version>4.0.2</version> <version>4.0.4</version>
<authors>Shaun Walker</authors> <authors>Shaun Walker</authors>
<owners>.NET Foundation</owners> <owners>.NET Foundation</owners>
<title>Oqtane PostgreSQL Provider</title> <title>Oqtane PostgreSQL Provider</title>
@ -12,7 +12,7 @@
<requireLicenseAcceptance>false</requireLicenseAcceptance> <requireLicenseAcceptance>false</requireLicenseAcceptance>
<license type="expression">MIT</license> <license type="expression">MIT</license>
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl> <projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.2</releaseNotes> <releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.4</releaseNotes>
<icon>icon.png</icon> <icon>icon.png</icon>
<tags>oqtane</tags> <tags>oqtane</tags>
</metadata> </metadata>

View File

@ -2,7 +2,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>net7.0</TargetFramework> <TargetFramework>net7.0</TargetFramework>
<Version>4.0.2</Version> <Version>4.0.4</Version>
<Product>Oqtane</Product> <Product>Oqtane</Product>
<Authors>Shaun Walker</Authors> <Authors>Shaun Walker</Authors>
<Company>.NET Foundation</Company> <Company>.NET Foundation</Company>
@ -10,7 +10,7 @@
<Copyright>.NET Foundation</Copyright> <Copyright>.NET Foundation</Copyright>
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl> <PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl> <PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.2</PackageReleaseNotes> <PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.4</PackageReleaseNotes>
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl> <RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
<RepositoryType>Git</RepositoryType> <RepositoryType>Git</RepositoryType>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>

View File

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd"> <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata> <metadata>
<id>Oqtane.Database.SqlServer</id> <id>Oqtane.Database.SqlServer</id>
<version>4.0.2</version> <version>4.0.4</version>
<authors>Shaun Walker</authors> <authors>Shaun Walker</authors>
<owners>.NET Foundation</owners> <owners>.NET Foundation</owners>
<title>Oqtane SQL Server Provider</title> <title>Oqtane SQL Server Provider</title>
@ -12,7 +12,7 @@
<requireLicenseAcceptance>false</requireLicenseAcceptance> <requireLicenseAcceptance>false</requireLicenseAcceptance>
<license type="expression">MIT</license> <license type="expression">MIT</license>
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl> <projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.2</releaseNotes> <releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.4</releaseNotes>
<icon>icon.png</icon> <icon>icon.png</icon>
<tags>oqtane</tags> <tags>oqtane</tags>
</metadata> </metadata>

View File

@ -2,7 +2,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>net7.0</TargetFramework> <TargetFramework>net7.0</TargetFramework>
<Version>4.0.2</Version> <Version>4.0.4</Version>
<Product>Oqtane</Product> <Product>Oqtane</Product>
<Authors>Shaun Walker</Authors> <Authors>Shaun Walker</Authors>
<Company>.NET Foundation</Company> <Company>.NET Foundation</Company>
@ -10,7 +10,7 @@
<Copyright>.NET Foundation</Copyright> <Copyright>.NET Foundation</Copyright>
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl> <PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl> <PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.2</PackageReleaseNotes> <PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.4</PackageReleaseNotes>
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl> <RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
<RepositoryType>Git</RepositoryType> <RepositoryType>Git</RepositoryType>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>

View File

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd"> <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata> <metadata>
<id>Oqtane.Database.Sqlite</id> <id>Oqtane.Database.Sqlite</id>
<version>4.0.2</version> <version>4.0.4</version>
<authors>Shaun Walker</authors> <authors>Shaun Walker</authors>
<owners>.NET Foundation</owners> <owners>.NET Foundation</owners>
<title>Oqtane SQLite Provider</title> <title>Oqtane SQLite Provider</title>
@ -12,7 +12,7 @@
<requireLicenseAcceptance>false</requireLicenseAcceptance> <requireLicenseAcceptance>false</requireLicenseAcceptance>
<license type="expression">MIT</license> <license type="expression">MIT</license>
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl> <projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.2</releaseNotes> <releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.4</releaseNotes>
<icon>icon.png</icon> <icon>icon.png</icon>
<tags>oqtane</tags> <tags>oqtane</tags>
</metadata> </metadata>

View File

@ -6,7 +6,7 @@
<!-- <TargetFrameworks>net7.0-android;net7.0-ios;net7.0-maccatalyst</TargetFrameworks> --> <!-- <TargetFrameworks>net7.0-android;net7.0-ios;net7.0-maccatalyst</TargetFrameworks> -->
<!-- <TargetFrameworks>$(TargetFrameworks);net7.0-tizen</TargetFrameworks> --> <!-- <TargetFrameworks>$(TargetFrameworks);net7.0-tizen</TargetFrameworks> -->
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<Version>4.0.2</Version> <Version>4.0.4</Version>
<Product>Oqtane</Product> <Product>Oqtane</Product>
<Authors>Shaun Walker</Authors> <Authors>Shaun Walker</Authors>
<Company>.NET Foundation</Company> <Company>.NET Foundation</Company>
@ -14,7 +14,7 @@
<Copyright>.NET Foundation</Copyright> <Copyright>.NET Foundation</Copyright>
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl> <PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl> <PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.2</PackageReleaseNotes> <PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.4</PackageReleaseNotes>
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl> <RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
<RepositoryType>Git</RepositoryType> <RepositoryType>Git</RepositoryType>
<RootNamespace>Oqtane.Maui</RootNamespace> <RootNamespace>Oqtane.Maui</RootNamespace>
@ -31,7 +31,7 @@
<ApplicationIdGuid>0E29FC31-1B83-48ED-B6E0-9F3C67B775D4</ApplicationIdGuid> <ApplicationIdGuid>0E29FC31-1B83-48ED-B6E0-9F3C67B775D4</ApplicationIdGuid>
<!-- Versions --> <!-- Versions -->
<ApplicationDisplayVersion>4.0.2</ApplicationDisplayVersion> <ApplicationDisplayVersion>4.0.4</ApplicationDisplayVersion>
<ApplicationVersion>1</ApplicationVersion> <ApplicationVersion>1</ApplicationVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">14.2</SupportedOSPlatformVersion> <SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">14.2</SupportedOSPlatformVersion>

View File

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd"> <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata> <metadata>
<id>Oqtane.Client</id> <id>Oqtane.Client</id>
<version>4.0.2</version> <version>4.0.4</version>
<authors>Shaun Walker</authors> <authors>Shaun Walker</authors>
<owners>.NET Foundation</owners> <owners>.NET Foundation</owners>
<title>Oqtane Framework</title> <title>Oqtane Framework</title>
@ -12,7 +12,7 @@
<requireLicenseAcceptance>false</requireLicenseAcceptance> <requireLicenseAcceptance>false</requireLicenseAcceptance>
<license type="expression">MIT</license> <license type="expression">MIT</license>
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl> <projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.2</releaseNotes> <releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.4</releaseNotes>
<icon>icon.png</icon> <icon>icon.png</icon>
<tags>oqtane</tags> <tags>oqtane</tags>
</metadata> </metadata>

View File

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd"> <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata> <metadata>
<id>Oqtane.Framework</id> <id>Oqtane.Framework</id>
<version>4.0.2</version> <version>4.0.4</version>
<authors>Shaun Walker</authors> <authors>Shaun Walker</authors>
<owners>.NET Foundation</owners> <owners>.NET Foundation</owners>
<title>Oqtane Framework</title> <title>Oqtane Framework</title>
@ -11,8 +11,8 @@
<copyright>.NET Foundation</copyright> <copyright>.NET Foundation</copyright>
<requireLicenseAcceptance>false</requireLicenseAcceptance> <requireLicenseAcceptance>false</requireLicenseAcceptance>
<license type="expression">MIT</license> <license type="expression">MIT</license>
<projectUrl>https://github.com/oqtane/oqtane.framework/releases/download/v4.0.2/Oqtane.Framework.4.0.2.Upgrade.zip</projectUrl> <projectUrl>https://github.com/oqtane/oqtane.framework/releases/download/v4.0.4/Oqtane.Framework.4.0.4.Upgrade.zip</projectUrl>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.2</releaseNotes> <releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.4</releaseNotes>
<icon>icon.png</icon> <icon>icon.png</icon>
<tags>oqtane framework</tags> <tags>oqtane framework</tags>
</metadata> </metadata>

View File

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd"> <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata> <metadata>
<id>Oqtane.Server</id> <id>Oqtane.Server</id>
<version>4.0.2</version> <version>4.0.4</version>
<authors>Shaun Walker</authors> <authors>Shaun Walker</authors>
<owners>.NET Foundation</owners> <owners>.NET Foundation</owners>
<title>Oqtane Framework</title> <title>Oqtane Framework</title>
@ -12,7 +12,7 @@
<requireLicenseAcceptance>false</requireLicenseAcceptance> <requireLicenseAcceptance>false</requireLicenseAcceptance>
<license type="expression">MIT</license> <license type="expression">MIT</license>
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl> <projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.2</releaseNotes> <releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v4.0.4</releaseNotes>
<icon>icon.png</icon> <icon>icon.png</icon>
<tags>oqtane</tags> <tags>oqtane</tags>
</metadata> </metadata>

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