Compare commits

..

152 Commits

Author SHA1 Message Date
1e0c7cf43d Merge pull request #2706 from oqtane/master
Merge pull request #2705 from oqtane/dev
2023-03-29 14:06:22 -04:00
7978c89731 Merge pull request #2705 from oqtane/dev
3.4.2 release
2023-03-29 14:05:57 -04:00
2e23e6e4d5 Merge pull request #2704 from sbwalker/dev
add defensive logic to package installer
2023-03-29 08:45:46 -04:00
82221f54c5 add defensive logic to package installer 2023-03-29 08:47:54 -04:00
3a79fa074a fix #2700 - translation installation 2023-03-28 15:52:07 -04:00
696c63c6d2 Merge pull request #2703 from sbwalker/dev
fix #2700 - translation installation
2023-03-28 15:49:54 -04:00
8f6dc52430 prepare for 3.4.2 release 2023-03-28 14:29:57 -04:00
c8a9ad9807 Merge pull request #2702 from sbwalker/dev
prepare for 3.4.2 release
2023-03-28 14:27:45 -04:00
a0933d07d8 made word casing consistent with other module messages and reduced length of message 2023-03-28 08:53:26 -04:00
6bf61e2008 Merge pull request #2701 from sbwalker/dev
made word casing consistent with other module messages and reduced length of message
2023-03-28 08:51:24 -04:00
36ecc55578 Merge pull request #2697 from leigh-pointer/PwdConstra
Modified Registration to display the Password requirments
2023-03-28 08:17:00 -04:00
47065299ca Merge pull request #2691 from Behnam-Emamian/dev
Code Cleanups
2023-03-28 08:14:09 -04:00
0f707a7607 Moved message to the notification.
To be honest, the message about the password should be visible at all times.
2023-03-25 11:47:15 +01:00
7590c5550f Merge pull request #2698 from sbwalker/dev
Fix #2696 - PermissionNames not appearing in PermissionGrid
2023-03-24 12:52:35 -04:00
3d23a5c79a Fix #2696 - PermissionNames not appearing in PermissionGrid 2023-03-24 12:54:46 -04:00
9aa0374dc2 Removed unused Using 2023-03-24 12:41:27 +01:00
058a191673 Modified Registration to display the Password requirments 2023-03-24 12:38:27 +01:00
5fbb9160f1 Code Cleanups 2023-03-21 00:45:17 +11:00
2c3dad0592 Merge pull request #2689 from sbwalker/dev
Fix #2687 - add Setters to Permissions property to provide improved backward compatibility
2023-03-16 13:24:27 -04:00
00f039d31e Fix #2687 - add Setters to Permissions property to provide improved backward compatibility 2023-03-16 13:26:18 -04:00
497ef1750b add defensive logic to Oqtane.Client for loading modules on which have not declared all dependencies on WebAssembly 2023-03-14 20:40:49 -04:00
7d4cd04ce9 Merge pull request #2684 from sbwalker/dev
add defensive logic to Oqtane.Client for loading modules on which have not declared all dependencies on WebAssembly
2023-03-14 20:39:23 -04:00
04c0b9d37d add defensive logic to Oqtane.Maui for loading modules which have not declared all dependencies 2023-03-14 20:37:42 -04:00
b9c16c0727 Merge pull request #2683 from sbwalker/dev
add defensive logic to Oqtane.Maui for loading modules which have not declared all dependencies
2023-03-14 20:35:55 -04:00
0a30f2b7e8 fix #2679 - fixed issue where ModuleDefinition cache properties were being overwritten (same issue as #2674 however implemented in ModuleController) 2023-03-14 11:49:38 -04:00
dbb1d53202 Merge pull request #2682 from sbwalker/dev
fix #2679 - fixed issue where ModuleDefinition cache properties were being overwritten (same issue as #2674 however implemented in ModuleController)
2023-03-14 11:48:01 -04:00
2c88f36e3d fix #2680 - issue when adding new site to existing installation 2023-03-14 10:26:51 -04:00
d91dcad774 Merge pull request #2681 from sbwalker/dev
fix #2680 - issue when adding new site to existing installation
2023-03-14 10:24:53 -04:00
6eb4ea2a2d Update README.md 2023-03-13 22:41:30 -04:00
ff6187c336 Merge pull request #2678 from oqtane/master
Merge pull request #2677 from oqtane/dev
2023-03-13 22:35:52 -04:00
1253dfe0c8 Merge pull request #2677 from oqtane/dev
3.4.1 release
2023-03-13 22:35:34 -04:00
c1f2f9a970 prepare for 3.4.1 2023-03-13 22:21:04 -04:00
8d5e7ed69f Merge pull request #2676 from sbwalker/dev
prepare for 3.4.1
2023-03-13 22:19:07 -04:00
3d3540f090 fixed issue where ModuleDefinition cache properties were being overwritten 2023-03-13 10:04:37 -04:00
61f1fcb99c Merge pull request #2674 from sbwalker/dev
fixed issue where ModuleDefinition cache properties were being overwritten
2023-03-13 10:02:53 -04:00
c7b576d6d3 Update README.md 2023-03-12 10:44:17 -04:00
ab3b1d5e46 Update README.md 2023-03-12 10:42:23 -04:00
ddff3faba9 Update README.md 2023-03-12 10:37:42 -04:00
f7bb8444da Merge pull request #2670 from oqtane/master
Merge pull request #2669 from oqtane/dev
2023-03-12 10:12:26 -04:00
92128974bb Merge pull request #2669 from oqtane/dev
3.4.0 release
2023-03-12 10:11:44 -04:00
4eb15d4806 include Site Map field in Site Settings 2023-03-11 17:42:37 -05:00
39cb3780c8 Merge pull request #2668 from sbwalker/dev
include Site Map field in Site Settings
2023-03-11 17:40:44 -05:00
81030f468b improve user profile error logging for external login 2023-03-11 14:03:24 -05:00
2032cb1ace Merge pull request #2667 from sbwalker/dev
improve user profile error logging for external login
2023-03-11 14:01:38 -05:00
5e4c91440e modify editmode parameter value in url to be more intuitive 2023-03-11 11:56:43 -05:00
13bbad863f Merge pull request #2666 from sbwalker/dev
modify editmode parameter value in url to be more intuitive
2023-03-11 11:54:48 -05:00
3065ed5094 fix issue with capturing user profile information during external login 2023-03-11 11:50:02 -05:00
9eb75cfff0 Merge pull request #2665 from sbwalker/dev
fix issue with capturing user profile information during external login
2023-03-11 11:48:07 -05:00
9305c99577 exclude hidden pages by default 2023-03-10 17:22:18 -05:00
9078da6937 Merge pull request #2663 from sbwalker/dev
exclude hidden pages by default
2023-03-10 17:20:25 -05:00
4c579639b9 avoid null reference error if list is null 2023-03-10 13:57:13 -05:00
b86472ab52 Merge pull request #2662 from sbwalker/dev
avoid null reference error if list is null
2023-03-10 13:55:28 -05:00
5e1ac485a0 #2655 - add support for capturing user profile infrmation from claims during external login 2023-03-10 10:14:57 -05:00
527c1a12f4 Merge pull request #2661 from sbwalker/dev
#2655 - add support for capturing user profile infrmation from claims during external login
2023-03-10 10:13:09 -05:00
ef4e99b3a7 further optimization of permissions - removed reference to Role to reduce API payload and minimize information disclosure 2023-03-10 08:28:37 -05:00
12a9635309 Merge pull request #2660 from sbwalker/dev
further optimization of permissions - removed reference to Role to reduce API payload and minimize information disclosure
2023-03-10 08:26:53 -05:00
78adb24a75 fix new installation issue 2023-03-09 16:54:44 -05:00
49955cf642 Merge pull request #2658 from sbwalker/dev
fix new installation issue
2023-03-09 16:52:51 -05:00
af3b289331 exclude legacy Permissions properties from serialization/API payload 2023-03-09 15:51:16 -05:00
d11591e5aa Merge pull request #2657 from sbwalker/dev
exclude legacy Permissions properties from serialization/API payload
2023-03-09 15:49:29 -05:00
9c6174e3f2 rolling back CSS changes so that it remains consistent with the Oqtane theme 2023-03-09 09:46:58 -05:00
09c2f74d52 Merge pull request #2654 from sbwalker/dev
rolling back CSS changes so that it remains consistent with the Oqtane theme
2023-03-09 09:45:15 -05:00
7d7e0254cb Merge pull request #2642 from leigh-pointer/RecycleBinDelete
Fix Clearing modules from the Recycle Bin
2023-03-09 08:08:58 -05:00
fe767afe9c Merge branch 'dev' into RecycleBinDelete 2023-03-09 11:49:22 +01:00
3378f0e4ee Procedures now checks on PageModuleId 2023-03-09 11:47:19 +01:00
c0341798ea Merge pull request #2653 from sbwalker/dev
include documentation to explain logic
2023-03-08 12:09:25 -05:00
fc114dc5db include documentation to explain logic 2023-03-08 12:11:11 -05:00
7107d844e1 fix #2640 - system should remain in edit mode when editing a page 2023-03-08 11:50:30 -05:00
59af0a817e Merge pull request #2652 from sbwalker/dev
fix #2640 - system should remain in edit mode when editing a page
2023-03-08 11:48:34 -05:00
9615eded85 Merge pull request #2644 from leigh-pointer/HideDeleteAllButtons
Hide the ActionDialog Button for  "Clear Notifications"
2023-03-08 10:50:13 -05:00
c51fa23fcb Merge pull request #2649 from leigh-pointer/PageChangeRecBin
Add OnPageChangePage to the Page and the Module Pagers in Recycle Bin
2023-03-08 10:50:04 -05:00
8737fd6f1e Merge pull request #2651 from sbwalker/dev
fx #2647 - error when creating new site in existing installation
2023-03-08 10:49:43 -05:00
0f109ab93a fx #2647 - error when creating new site in existing installation 2023-03-08 10:51:38 -05:00
63df2742db initialize SiteId in Permission constructor 2023-03-08 08:43:45 -05:00
7b7811f8ad Merge pull request #2650 from sbwalker/dev
initialize SiteId in Permission constructor
2023-03-08 08:42:13 -05:00
80f74b9939 Add OnPageChangePage to the Page and the Module Pagers
Now when deleting Pages or Modules the current page is shown and not reset to the first page.
2023-03-08 13:55:27 +01:00
1f29f77f66 fix #2624 - permission grid behavior issues 2023-03-06 12:20:20 -05:00
dd7da5f354 Merge pull request #2645 from sbwalker/dev
fix #2624 - permission grid behavior issues
2023-03-06 12:19:01 -05:00
49b30da697 Hide the ActionDialog Button for "Clear Notifications"
If there are no Notifications then the buttons is not displayed.
2023-03-06 09:13:25 +01:00
90ed767d75 Fix Clearing modules from the Recycle Bin
Modules and all related records now correctly deleted from the database.
2023-03-06 06:26:01 +01:00
7871f0f3ce Merge pull request #2628 from thabaum/dev
Fixes #2627 #2631 #2630 #2629 #2632  #2635 - Dev Branch Module and Theme Template Issues
2023-03-05 17:53:20 -05:00
a60cf40a3c inverts color of text within "main" <div> 2023-03-05 09:50:56 -08:00
f4eb2f6726 Merge pull request #2637 from sbwalker/dev
fix localization issue in Scheduled Jobs
2023-03-05 08:55:06 -05:00
cfe87a802e fix localization issue in Scheduled Jobs 2023-03-05 08:56:35 -05:00
6c90ec812f Fix - unable to see module actions dropdown toggle 2023-03-04 15:24:43 -08:00
196d611c1c Fix issue with navbar 2023-03-04 13:13:49 -08:00
ff41cb2735 Missing using directive Oqtane.Repository 2023-03-04 10:45:32 -08:00
fb11674301 Missing - Using Oqtane.Repository 2023-03-04 10:44:19 -08:00
b9e7f4530c Update Theme Project to use [RootFolder] 2023-03-04 10:35:45 -08:00
27049687bf Use [RootFolder] for Oqtane.Server Project 2023-03-04 10:34:12 -08:00
13503edc63 Removed extra { } 2023-03-04 09:50:49 -08:00
d33f82d969 prepare for 3.4.0 release 2023-03-03 15:45:56 -05:00
177632eee0 Merge pull request #2622 from sbwalker/dev
prepare for 3.4.0 release
2023-03-03 15:44:08 -05:00
ca0de5258e Merge pull request #2620 from leigh-pointer/DeletePermissionModDef
Fix Correct Permission Delete when ModuleDef is deleted #2619
2023-03-02 15:33:25 -05:00
1de788bc26 Merge pull request #2621 from sbwalker/dev
#2618 - add backward compatibility for permissions optimizations
2023-03-02 15:33:11 -05:00
2b41909d47 #2618 - add backward compatibility for permissions optimizations 2023-03-02 15:34:42 -05:00
e23a9f22dd Fix Correct Permission Delete when ModuleDef is deleted #2619
Added PermissionsRepository to delete the Module permissions when the Module Definition is deleted.
2023-03-02 06:58:19 +01:00
465b7850b7 Fix #2614 - ability to add module to page 2023-03-01 10:05:14 -05:00
a0f2eedd7f Merge pull request #2615 from sbwalker/dev
Fix #2614 - ability to add module to page
2023-03-01 10:03:30 -05:00
8605e3ca5a Major refactoring replacing permission strings with permission collections. These changes will require extensive regression testing. These changes may include breaking changes which will need to be identified and resolved to provide backward compatibility. 2023-02-28 17:59:21 -05:00
dd893e6d48 Merge pull request #2612 from sbwalker/dev
Major refactoring replacing permission strings with permission collections. These changes will require extensive regression testing. These changes may include breaking changes which will need to be identified and resolved to provide backward compatibility.
2023-02-28 17:57:54 -05:00
c4cd1a5a54 Merge pull request #2610 from leigh-pointer/DeleteModDefPageMod
Fix for deleting a ModuleDefinition and related records  #2602
2023-02-24 14:21:22 -05:00
94152651fc Merge pull request #2609 from Behnam-Emamian/dev
extends watching *.dll files
2023-02-24 14:20:44 -05:00
563ea76192 Merge pull request #2611 from sbwalker/dev
explicity specify optional and reload parameters
2023-02-24 14:19:27 -05:00
4913fab0b3 explicity specify optional and reload parameters 2023-02-24 14:21:03 -05:00
b49d011edf Fix for deleting a ModuleDefinition and related records #2602
We then find all Module items that have a ModuleDefinitionName property that matches the ModuleDefinitionName of the item to be removed, and remove them one by one. For each Module item to be removed, we find the PageModule items associated with it, remove them from the pageModules list, and then remove the Module item itself from the modules list.
2023-02-24 11:44:12 +01:00
6e04281b03 extends watching dll files
extends watching group to include *.dll files and exclude the ones cause an infinite loop.
2023-02-24 11:20:05 +11:00
f2df8e96db fix #2567 - migrate tenant connection string details from database to appsettings.json 2023-02-23 16:29:15 -05:00
c6dd7605b2 Merge pull request #2608 from sbwalker/dev
fix #2567 - migrate tenant connection string details from database to appsettings.json
2023-02-23 16:28:16 -05:00
71dd00da0f Merge pull request #2605 from Behnam-Emamian/dev
add AddByteColumn to add tinyint to the database table.
2023-02-21 07:54:20 -05:00
da48ca884d Merge pull request #2606 from sbwalker/dev
add sitemap generator which outputs all public pages and also includes an ISitemap interface for modules
2023-02-20 08:34:15 -05:00
8c6c66fb11 add sitemap generator which outputs all public pages and also includes an ISitemap interface for modules 2023-02-20 08:35:46 -05:00
f333b57310 add AddByteColumn to add tinyint to the database table 2023-02-20 00:20:19 +11:00
d1d00e6c98 Merge pull request #2601 from leigh-pointer/ModDefSettingsDelete
Fix for Missing Delete ModuleDefinition settings #1966
2023-02-17 09:15:44 -05:00
52300e680a Fix for Missing Delete ModuleDefinition settings #1966
Added ISettingRepository _settings  to the public ModuleDefinitionRepository method and updated the DeleteModuleDefinition with _settings.DeleteSettings(EntityNames.ModuleDefinition, moduleDefinitionId);
2023-02-16 13:38:42 +01:00
b3f7353582 Merge pull request #2599 from sbwalker/dev
add defensive logic to querystring parser to handle duplicate parameters
2023-02-15 15:20:12 -05:00
7db6b82a1a add defensive logic to querystring parser to handle duplicate parameters 2023-02-15 15:21:50 -05:00
a50a13374f improve initialization logic in FileManager which could sometimes result in Upload button not being displayed when the component was initially loaded 2023-02-15 15:06:50 -05:00
3952fe5a72 Merge pull request #2598 from sbwalker/dev
improve initialization logic in FileManager which could sometimes result in Upload button not being displayed when the component was initially loaded
2023-02-15 15:05:17 -05:00
2e61a43e4f fix #2596 - fix EF Core tracking error when updating a file in a folder which has a Capacity specified 2023-02-15 12:43:18 -05:00
ebe03e9310 Merge pull request #2597 from sbwalker/dev
fix #2596 - fix EF Core tracking error when updating a file in a folder which has a Capacity specified
2023-02-15 12:42:00 -05:00
11dd3ce110 adding Oqtane.Server project back to module and theme external template solutions 2023-02-09 17:45:45 -05:00
1919c24959 Merge pull request #2593 from sbwalker/dev
adding Oqtane.Server project back to module and theme external template solutions
2023-02-09 17:44:27 -05:00
aa80f31e52 fix #2570 - do not allow the term "oqtane" to be used as an organization or module/theme name (to avoid namespace issues). 2023-02-09 16:26:20 -05:00
6d8400e72f Merge pull request #2592 from sbwalker/dev
fix #2570 - do not allow the term "oqtane" to be used as an organization or module/theme name (to avoid namespace issues).
2023-02-09 16:25:08 -05:00
fa8d0c91fc added new methods for managing visitor settings (for personalization) 2023-02-08 17:43:55 -05:00
e91ff95712 Merge pull request #2591 from sbwalker/dev
added new methods for managing visitor settings (for personalization)
2023-02-08 17:42:35 -05:00
0883a8dbff optimize Split() statements for consistency 2023-02-08 16:51:45 -05:00
0db297d1cd Merge pull request #2590 from sbwalker/dev
optimize Split() statements for consistency
2023-02-08 16:50:23 -05:00
db73052ee5 allow system log to be cleared 2023-02-08 14:45:20 -05:00
8b95069610 Merge pull request #2589 from sbwalker/dev
allow system log to be cleared
2023-02-08 14:43:58 -05:00
2a12744cd5 added toggle to show/hide connection string in Site Settings 2023-02-08 08:29:50 -05:00
1df4059284 Merge pull request #2588 from sbwalker/dev
added toggle to show/hide connection string in Site Settings
2023-02-08 08:28:34 -05:00
475894b680 fix #2584 - added IsDeleted columns back to Folder and File tables to preserve compatibility for SQLite 2023-02-08 08:05:25 -05:00
1663bf8e52 Merge pull request #2587 from sbwalker/dev
fix #2584 - added IsDeleted columns back to Folder and File tables to preserve compatibility for SQLite
2023-02-08 08:04:58 -05:00
ffca1d2486 refactor visitor cookie name into a shared constant 2023-02-07 16:26:23 -05:00
eb876845ff Merge pull request #2585 from sbwalker/dev
refactor visitor cookie name into a shared constant
2023-02-07 16:25:05 -05:00
02c134bf4b Merge pull request #2580 from markdav-is/patch-3
Make ActiveDatabase setter public
2023-02-06 16:50:55 -05:00
af55c11aa0 Merge pull request #2582 from sbwalker/dev
fix #2574 - check for null ModuleDefinition reference  when loding permissions in PageModuleRepository (credit @beolafsen)
2023-02-06 16:48:40 -05:00
33bc6adcb5 fix #2574 - check for null ModuleDefinition reference when loding permissions in PageModuleRepository (credit @beolafsen) 2023-02-06 16:49:45 -05:00
56e4dcc11e fix #2578 - error notification sent via email includes direct link to specific log item, however redirect was causing an infinite loop. This resolves the problem and also preserves url querystring parameters during login/logout. 2023-02-06 16:44:25 -05:00
467cf7620e Merge pull request #2581 from sbwalker/dev
fix #2578 - error notification sent via email includes direct link to specific log item, however redirect was causing an infinite loop. This resolves the problem and also preserves url querystring parameters during login/logout.
2023-02-06 16:43:23 -05:00
85ac8dd701 Make ActiveDatabase setter public
We have two cases where we need to override the active database:  Unit Testing and added GraphQL.  In both of these cases, we have a database context that is in a different scope than the automatically assigned active database during normal Oqtane startup.  Our work-around has been to make this setter public.  Unless there is a better solution to our cases, I feel this change would be useful for others as well.
2023-02-04 09:04:54 -08:00
1f2ad4e884 Suppress unauthorized visitor logging as it is usually caused by clients that do not support cookies 2023-02-03 16:12:13 -05:00
cf2d9af664 Merge pull request #2579 from sbwalker/dev
Suppress unauthorized visitor logging as it is usually caused by clients that do not support cookies
2023-02-03 16:10:57 -05:00
7a105047e9 Fixed issue where TenantMiddleware was not rewriting the Url path for the new File Server when running on an Alias Path which resulted in a 404 when serving files 2023-01-23 15:16:08 -05:00
bc8bdef37d Merge pull request #2571 from sbwalker/dev
Fixed issue where TenantMiddleware was not rewriting the Url path for the new File Server when running on an Alias Path which resulted in a 404 when serving files
2023-01-23 15:15:05 -05:00
fd0519b955 Update README.md 2023-01-14 15:17:38 -05:00
d5ffb56fa8 Update README.md 2023-01-14 15:15:33 -05:00
d6cce9e2d8 Update README.md 2023-01-14 15:08:20 -05:00
08ec46637f Update README.md 2023-01-14 15:04:31 -05:00
f596795792 Update README.md 2023-01-14 15:03:46 -05:00
159 changed files with 2493 additions and 1613 deletions

View File

@ -55,7 +55,7 @@
else
{
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="connectionstring" HelpText="Enter a complete connection string including all parameters and delimiters" ResourceKey="ConnectionString">String:</Label>
<Label Class="col-sm-3" For="connectionstring" HelpText="Enter a complete connection string including all parameters and delimiters" ResourceKey="ConnectionString">Settings:</Label>
<div class="col-sm-9">
<textarea id="connectionstring" class="form-control" @bind="@_connectionString" rows="3"></textarea>
</div>

View File

@ -7,7 +7,7 @@
<div class="row">
@foreach (var p in _pages)
{
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions))
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.PermissionList))
{
string url = NavigateUrl(p.Path);
<div class="col-md-2 mx-auto text-center mb-3">

View File

@ -62,8 +62,7 @@
<div class="row mb-1 align-items-center">
<div class="col-sm-12">
<Label Class="col-sm-3" For="permissions" HelpText="Select the permissions you want for the folder" ResourceKey="Permissions">Permissions: </Label>
<PermissionGrid EntityName="@EntityNames.Folder" PermissionNames="@(PermissionNames.Browse + "," + PermissionNames.View + "," + PermissionNames.Edit)" Permissions="@_permissions" @ref="_permissionGrid" />
<PermissionGrid EntityName="@EntityNames.Folder" PermissionNames="@(PermissionNames.Browse + "," + PermissionNames.View + "," + PermissionNames.Edit)" PermissionList="@_permissions" @ref="_permissionGrid" />
</div>
</div>
</div>
@ -99,7 +98,7 @@
private string _imagesizes = string.Empty;
private string _capacity = "0";
private bool _isSystem;
private string _permissions = string.Empty;
private List<Permission> _permissions = null;
private string _createdBy;
private DateTime _createdOn;
private string _modifiedBy;
@ -131,7 +130,7 @@
_imagesizes = folder.ImageSizes;
_capacity = folder.Capacity.ToString();
_isSystem = folder.IsSystem;
_permissions = folder.Permissions;
_permissions = folder.PermissionList;
_createdBy = folder.CreatedBy;
_createdOn = folder.CreatedOn;
_modifiedBy = folder.ModifiedBy;
@ -196,7 +195,7 @@
folder.ImageSizes = _imagesizes;
folder.Capacity = int.Parse(_capacity);
folder.IsSystem = _isSystem;
folder.Permissions = _permissionGrid.GetPermissions();
folder.PermissionList = _permissionGrid.GetPermissionList();
if (_folderId != -1)
{

View File

@ -20,15 +20,6 @@ else
<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="translated" HelpText="Specify If You Wish To Select Languages That Have Translations Installed" ResourceKey="Translated">Translated?</Label>
<div class="col-sm-9">
<select id="translated" class="form-select" value="@_translated" @onchange="(e => TranslatedChanged(e))" required>
<option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option>
</select>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="name" HelpText="Name Of The Language" ResourceKey="Name">Name:</Label>
<div class="col-sm-9">
<select id="_code" class="form-select" @bind="@_code" required>
@ -73,7 +64,6 @@ else
private ElementReference form;
private bool validated = false;
private string _translated = "True";
private string _code = "-";
private string _default = "False";
private List<string> _languages;
@ -91,18 +81,11 @@ else
private async Task LoadCultures()
{
_cultures = await LocalizationService.GetCulturesAsync(bool.Parse(_translated));
_cultures = await LocalizationService.GetCulturesAsync(false);
_cultures = _cultures.Where(c => !c.Name.Equals(Constants.DefaultCulture) && !_languages.Contains(c.Name));
_code = "-";
}
private async void TranslatedChanged(ChangeEventArgs e)
{
_translated = (string)e.Value;
await LoadCultures();
StateHasChanged();
}
private async Task SaveLanguage()
{
validated = true;

View File

@ -35,14 +35,22 @@ else
{
<td>@((string.IsNullOrEmpty(context.Version)) ? "---" : context.Version)</td>
<td>
@switch (TranslationAvailable(context.Code, context.Version))
@{
var translation = TranslationAvailable(context.Code, context.Version);
}
@if (translation != null)
{
case "install":
<button type="button" class="btn btn-success" @onclick=@(async () => await GetPackage(context.Code))>@SharedLocalizer["Download"]</button>
break;
case "upgrade":
<button type="button" class="btn btn-success" @onclick=@(async () => await GetPackage(context.Code))>@SharedLocalizer["Upgrade"]</button>
break;
if (string.IsNullOrEmpty(context.Version))
{
<button type="button" class="btn btn-success" @onclick=@(async () => await GetPackage(context.Code, translation.Version))>@SharedLocalizer["Download"]</button>
}
else
{
if (Version.Parse(translation.Version).CompareTo(Version.Parse(context.Version)) > 0)
{
<button type="button" class="btn btn-success" @onclick=@(async () => await GetPackage(context.Code, translation.Version))>@SharedLocalizer["Upgrade"]</button>
}
}
}
</td>
}
@ -134,38 +142,16 @@ else
}
}
private string TranslationAvailable(string code, string version)
private Package TranslationAvailable(string code, string version)
{
if (_packages != null)
{
var package = _packages.Where(item => item.PackageId == (Constants.PackageId + "." + code)).FirstOrDefault();
if (package != null)
{
// package version needs to match current framework version
if (Version.Parse(package.Version).CompareTo(Version.Parse(Constants.Version)) == 0)
{
if (string.IsNullOrEmpty(version))
{
return "install";
}
else
{
if (Version.Parse(package.Version).CompareTo(Version.Parse(version)) > 0)
{
return "upgrade";
}
}
}
}
}
return "";
return _packages?.FirstOrDefault(item => item.PackageId == (Constants.PackageId + "." + code));
}
private async Task GetPackage(string code)
private async Task GetPackage(string code, string version)
{
try
{
_package = await PackageService.GetPackageAsync(Constants.PackageId + "." + code, Constants.Version);
_package = await PackageService.GetPackageAsync(Constants.PackageId + "." + code, version);
StateHasChanged();
}
catch (Exception ex)
@ -204,7 +190,7 @@ else
try
{
await PackageService.InstallPackagesAsync();
AddModuleMessage(string.Format(Localizer["Success.Translation.Install"], NavigateUrl("admin/system")), MessageType.Success);
AddModuleMessage(string.Format(Localizer["Success.Language.Install"], NavigateUrl("admin/system")), MessageType.Success);
_install = false;
StateHasChanged();
}

View File

@ -1,3 +1,4 @@
@using System.Net
@namespace Oqtane.Modules.Admin.Login
@inherits ModuleBase
@inject NavigationManager NavigationManager
@ -205,7 +206,7 @@
var authstateprovider = (IdentityAuthenticationStateProvider)ServiceProvider
.GetService(typeof(IdentityAuthenticationStateProvider));
authstateprovider.NotifyAuthenticationChanged();
NavigationManager.NavigateTo(NavigateUrl(_returnUrl, true));
NavigationManager.NavigateTo(NavigateUrl(WebUtility.UrlDecode(_returnUrl), true));
}
else
{

View File

@ -106,12 +106,6 @@ else
{
try
{
// external link to log item will display Details component
if (PageState.QueryString.ContainsKey("id") && int.TryParse(PageState.QueryString["id"], out int id))
{
NavigationManager.NavigateTo(EditUrl(PageState.Page.Path, ModuleState.ModuleId, "Detail", $"/{id}"));
}
if (UrlParameters.ContainsKey("level"))
{
_level = UrlParameters["level"];
@ -241,4 +235,15 @@ else
_page = page;
}
protected override void OnAfterRender(bool firstRender)
{
if (firstRender)
{
// external link to log item will display Details component
if (PageState.QueryString.ContainsKey("id") && int.TryParse(PageState.QueryString["id"], out int id))
{
NavigationManager.NavigateTo(EditUrl(PageState.Page.Path, ModuleState.ModuleId, "Detail", $"/{id}"));
}
}
}
}

View File

@ -174,7 +174,7 @@ else
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" && 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 void TemplateChanged(ChangeEventArgs e)

View File

@ -139,7 +139,7 @@
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" && 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 void TemplateChanged(ChangeEventArgs e)

View File

@ -9,7 +9,9 @@
@inject IStringLocalizer<Edit> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer
<TabStrip>
@if (_initialized)
{
<TabStrip>
<TabPanel Name="Definition" ResourceKey="Definition">
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
<div class="container">
@ -95,9 +97,10 @@
<TabPanel Name="Permissions" ResourceKey="Permissions">
<div class="container">
<div class="row mb-1 align-items-center">
<PermissionGrid EntityName="@EntityNames.ModuleDefinition" PermissionNames="@PermissionNames.Utilize" Permissions="@_permissions" @ref="_permissionGrid" />
<PermissionGrid EntityName="@EntityNames.ModuleDefinition" PermissionNames="@PermissionNames.Utilize" PermissionList="@_permissions" @ref="_permissionGrid" />
</div>
</div>
<br />
<button type="button" class="btn btn-success" @onclick="SaveModuleDefinition">@SharedLocalizer["Save"]</button>
<NavLink class="btn btn-secondary" href="@NavigateUrl()">@SharedLocalizer["Cancel"]</NavLink>
</TabPanel>
@ -149,10 +152,10 @@
<br />
}
</TabPanel>
</TabStrip>
</TabStrip>
@if (_package != null)
{
@if (_package != null)
{
<div class="app-actiondialog">
<div class="modal" tabindex="-1" role="dialog">
<div class="modal-dialog">
@ -189,9 +192,11 @@
</div>
</div>
</div>
}
}
@code {
private bool _initialized = false;
private ElementReference form;
private bool validated = false;
private int _moduleDefinitionId;
@ -206,7 +211,7 @@
private string _contact = "";
private string _license = "";
private string _runtimes = "";
private string _permissions;
private List<Permission> _permissions = null;
private string _createdby;
private DateTime _createdon;
private string _modifiedby;
@ -242,7 +247,7 @@
_contact = moduleDefinition.Contact;
_license = moduleDefinition.License;
_runtimes = moduleDefinition.Runtimes;
_permissions = moduleDefinition.Permissions;
_permissions = moduleDefinition.PermissionList;
_createdby = moduleDefinition.CreatedBy;
_createdon = moduleDefinition.CreatedOn;
_modifiedby = moduleDefinition.ModifiedBy;
@ -262,6 +267,8 @@
}
_languages = _languages.OrderBy(item => item.Name).ToList();
}
_initialized = true;
}
}
catch (Exception ex)
@ -292,7 +299,7 @@
{
moduledefinition.Categories = _categories;
}
moduledefinition.Permissions = _permissionGrid.GetPermissions();
moduledefinition.PermissionList = _permissionGrid.GetPermissionList();
await ModuleDefinitionService.UpdateModuleDefinitionAsync(moduledefinition);
await logger.LogInformation("ModuleDefinition Saved {ModuleDefinition}", moduledefinition);
NavigationManager.NavigateTo(NavigateUrl());

View File

@ -46,7 +46,7 @@
<select id="page" class="form-select" @bind="@_pageId" required>
@foreach (Page p in PageState.Pages)
{
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions))
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.PermissionList))
{
<option value="@p.PageId">@(new string('-', p.Level * 2))@(p.Name)</option>
}
@ -62,7 +62,7 @@
{
<div class="container">
<div class="row mb-1 align-items-center">
<PermissionGrid EntityName="@EntityNames.Module" PermissionNames="@_permissionNames" Permissions="@_permissions" @ref="_permissionGrid" />
<PermissionGrid EntityName="@EntityNames.Module" PermissionNames="@_permissionNames" PermissionList="@_permissions" @ref="_permissionGrid" />
</div>
</div>
@ -101,7 +101,7 @@
private string _containerType;
private string _allPages = "false";
private string _permissionNames = "";
private string _permissions = null;
private List<Permission> _permissions = null;
private string _pageId;
private PermissionGrid _permissionGrid;
private Type _moduleSettingsType;
@ -123,7 +123,7 @@
_containers = ThemeService.GetContainerControls(_themes, PageState.Page.ThemeType);
_containerType = ModuleState.ContainerType;
_allPages = ModuleState.AllPages.ToString();
_permissions = ModuleState.Permissions;
_permissions = ModuleState.PermissionList;
_pageId = ModuleState.PageId.ToString();
createdby = ModuleState.CreatedBy;
createdon = ModuleState.CreatedOn;
@ -207,7 +207,7 @@
var module = ModuleState;
module.AllPages = bool.Parse(_allPages);
module.PageModuleId = ModuleState.PageModuleId;
module.Permissions = _permissionGrid.GetPermissions();
module.PermissionList = _permissionGrid.GetPermissionList();
await ModuleService.UpdateModuleAsync(module);
if (_moduleSettingsType != null)

View File

@ -183,7 +183,7 @@
private string _themetype = string.Empty;
private string _containertype = string.Empty;
private string _icon = string.Empty;
private string _permissions = string.Empty;
private string _permissions = null;
private PermissionGrid _permissionGrid;
private Type _themeSettingsType;
private object _themeSettings;
@ -202,7 +202,6 @@
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
_containertype = PageState.Site.DefaultContainerType;
_children = PageState.Pages.Where(item => item.ParentId == null).ToList();
_permissions = string.Empty;
ThemeSettings();
}
catch (Exception ex)
@ -222,7 +221,7 @@
{
foreach (Page p in PageState.Pages.Where(item => item.ParentId == null))
{
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions))
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.PermissionList))
{
_children.Add(p);
}
@ -232,7 +231,7 @@
{
foreach (Page p in PageState.Pages.Where(item => item.ParentId == int.Parse(_parentid)))
{
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions))
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.PermissionList))
{
_children.Add(p);
}
@ -378,7 +377,7 @@
page.DefaultContainerType = string.Empty;
}
page.Icon = (_icon == null ? string.Empty : _icon);
page.Permissions = _permissionGrid.GetPermissions();
page.PermissionList = _permissionGrid.GetPermissionList();
page.IsPersonalizable = (_ispersonalizable == null ? false : Boolean.Parse(_ispersonalizable));
page.UserId = null;
page.Meta = _meta;

View File

@ -158,7 +158,7 @@
{
<div class="container">
<div class="row mb-1 align-items-center">
<PermissionGrid EntityName="@EntityNames.Page" Permissions="@_permissions" @ref="_permissionGrid" />
<PermissionGrid EntityName="@EntityNames.Page" PermissionList="@_permissions" @ref="_permissionGrid" />
</div>
</div>
}
@ -174,8 +174,8 @@
<th>@Localizer["ModuleDefinition"]</th>
</Header>
<Row>
<td><ActionLink Action="Settings" Text="Edit" ModuleId="@context.ModuleId" Security="SecurityAccessLevel.Edit" Permissions="@context.Permissions" ResourceKey="ModuleSettings" /></td>
<td><ActionDialog Header="Delete Module" Message="Are You Sure You Wish To Delete This Module?" Action="Delete" Security="SecurityAccessLevel.Edit" Permissions="@context.Permissions" Class="btn btn-danger" OnClick="@(async () => await DeleteModule(context))" ResourceKey="DeleteModule" /></td>
<td><ActionLink Action="Settings" Text="Edit" 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>@context.Title</td>
<td>@context.ModuleDefinition?.Name</td>
</Row>
@ -221,7 +221,7 @@
private string _themetype;
private string _containertype = "-";
private string _icon;
private string _permissions = null;
private List<Permission> _permissions = null;
private string _createdby;
private DateTime _createdon;
private string _modifiedby;
@ -292,7 +292,7 @@
_containertype = PageState.Site.DefaultContainerType;
}
_icon = page.Icon;
_permissions = page.Permissions;
_permissions = page.PermissionList;
_createdby = page.CreatedBy;
_createdon = page.CreatedOn;
_modifiedby = page.ModifiedBy;
@ -339,7 +339,7 @@
{
foreach (Page p in PageState.Pages.Where(item => item.ParentId == null))
{
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions))
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.PermissionList))
{
_children.Add(p);
}
@ -349,7 +349,7 @@
{
foreach (Page p in PageState.Pages.Where(item => item.ParentId == int.Parse(_parentid)))
{
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions))
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.PermissionList))
{
_children.Add(p);
}
@ -509,7 +509,7 @@
page.DefaultContainerType = string.Empty;
}
page.Icon = _icon ?? string.Empty;
page.Permissions = _permissionGrid.GetPermissions();
page.PermissionList = _permissionGrid.GetPermissionList();
page.IsPersonalizable = (_ispersonalizable != null && Boolean.Parse(_ispersonalizable));
page.UserId = null;
page.Meta = _meta;

View File

@ -22,7 +22,7 @@ else
}
else
{
<Pager Items="@_pages.Where(item => item.IsDeleted)">
<Pager Items="@_pages.Where(item => item.IsDeleted)" CurrentPage="@_pagePage.ToString()" OnPageChange="OnPageChangePage">
<Header>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
@ -50,7 +50,7 @@ else
}
else
{
<Pager Items="@_modules.Where(item => item.IsDeleted)">
<Pager Items="@_modules.Where(item => item.IsDeleted)" CurrentPage="@_pageModule.ToString()" OnPageChange="OnPageChangeModule">
<Header>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
@ -78,7 +78,8 @@ else
@code {
private List<Page> _pages;
private List<Module> _modules;
private int _pagePage = 1;
private int _pageModule = 1;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin;
protected override async Task OnInitializedAsync()
@ -185,7 +186,7 @@ else
await PageModuleService.DeletePageModuleAsync(module.PageModuleId);
// check if there are any remaining module instances in the site
if (!_modules.Exists(item => item.ModuleId == module.ModuleId))
if (!_modules.Exists (item => item.ModuleId == module.ModuleId && item.PageModuleId != module.PageModuleId))
{
await ModuleService.DeleteModuleAsync(module.ModuleId);
}
@ -206,12 +207,14 @@ else
try
{
ModuleInstance.ShowProgressIndicator();
foreach (Module module in _modules.Where(item => item.IsDeleted))
foreach (Module module in _modules.Where(item => item.IsDeleted).ToList())
{
await PageModuleService.DeletePageModuleAsync(module.PageModuleId);
// DeletePageModuleAsync does not update _modules so remove it.
_modules.Remove(module);
// check if there are any remaining module instances in the site
if (!_modules.Exists(item => item.ModuleId == module.ModuleId))
if (!_modules.Exists(item => item.ModuleId == module.ModuleId && item.PageModuleId != module.PageModuleId))
{
await ModuleService.DeleteModuleAsync(module.ModuleId);
}
@ -229,4 +232,12 @@ else
ModuleInstance.HideProgressIndicator();
}
}
private void OnPageChangePage(int page)
{
_pagePage = page;
}
private void OnPageChangeModule(int page)
{
_pageModule = page;
}
}

View File

@ -4,6 +4,7 @@
@inject IUserService UserService
@inject IStringLocalizer<Index> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer
@inject ISettingService SettingService
@if (PageState.Site.AllowRegistration)
{
@ -15,7 +16,7 @@
<ModuleMessage Message="@Localizer["Info.Registration.Exists"]" Type="MessageType.Info" />
</Authorized>
<NotAuthorized>
<ModuleMessage Message="@Localizer["Info.Registration.InvalidEmail"]" Type="MessageType.Info" />
<ModuleMessage Message="@_passwordconstruction" Type="MessageType.Info" />
<form @ref="form" class="@(validated ? "was-validated" : "needs-validation")" novalidate>
<div class="container">
<div class="row mb-1 align-items-center">
@ -78,11 +79,44 @@ else
private string _email = 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;
protected override async Task OnInitializedAsync()
{
var settings = await SettingService.GetSiteSettingsAsync(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()
{
_togglepassword = SharedLocalizer["ShowPassword"];
}
private async Task Register()

View File

@ -78,7 +78,7 @@
<option value="-">&lt;@Localizer["Not Specified"]&gt;</option>
@foreach (Page page in PageState.Pages)
{
if (UserSecurity.ContainsRole(page.Permissions, PermissionNames.View, RoleNames.Everyone))
if (UserSecurity.ContainsRole(page.PermissionList, PermissionNames.View, RoleNames.Everyone))
{
<option value="@(page.PageId)">@(new string('-', page.Level * 2))@(page.Name)</option>
}
@ -95,6 +95,12 @@
</select>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="sitemap" HelpText="The site map url for this site which can be submitted to search engines for indexing" ResourceKey="SiteMap">Site Map: </Label>
<div class="col-sm-9">
<input id="sitemap" class="form-control" @bind="@_sitemap" required disabled />
</div>
</div>
</div>
<Section Name="SMTP" Heading="SMTP Settings" ResourceKey="SMTPSettings">
<div class="container">
@ -267,16 +273,16 @@
</div>
</div>
</Section>
<Section Name="TenantInformation" Heading="Tenant Information" ResourceKey="TenantInformation">
<Section Name="TenantInformation" Heading="Database" ResourceKey="TenantInformation">
<div class="container">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="tenant" HelpText="The tenant for the site" ResourceKey="Tenant">Tenant: </Label>
<Label Class="col-sm-3" For="tenant" HelpText="The name of the database used for the site" ResourceKey="Tenant">Database: </Label>
<div class="col-sm-9">
<input id="tenant" class="form-control" @bind="@_tenant" readonly />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="database" HelpText="The database for the tenant" ResourceKey="Database">Database: </Label>
<Label Class="col-sm-3" For="database" HelpText="The type of database" ResourceKey="Database">Type: </Label>
<div class="col-sm-9">
<input id="database" class="form-control" @bind="@_database" readonly />
</div>
@ -284,7 +290,7 @@
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="connectionstring" HelpText="The connection information for the database" ResourceKey="ConnectionString">Connection: </Label>
<div class="col-sm-9">
<textarea id="connectionstring" class="form-control" @bind="@_connectionstring" rows="2" readonly></textarea>
<input id="connectionstring" class="form-control" @bind="@_connectionstring" readonly />
</div>
</div>
</div>
@ -321,6 +327,7 @@
private string _containertype = "-";
private string _admincontainertype = "-";
private string _homepageid = "-";
private string _sitemap = "";
private string _smtphost = string.Empty;
private string _smtpport = string.Empty;
private string _smtpssl = "False";
@ -361,6 +368,7 @@
_runtime = site.Runtime;
_prerender = site.RenderMode.Replace(_runtime, "");
_isdeleted = site.IsDeleted.ToString();
_sitemap = PageState.Alias.Protocol + PageState.Alias.Name + "/pages/sitemap.xml";
await GetAliases();

View File

@ -103,7 +103,7 @@ else
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="tenant" HelpText="Select the tenant for the site" ResourceKey="Tenant">Tenant: </Label>
<Label Class="col-sm-3" For="tenant" HelpText="Select the database for the site" ResourceKey="Tenant">Database: </Label>
<div class="col-sm-9">
<select id="tenant" class="form-select" @onchange="(e => TenantChanged(e))" required>
<option value="-">&lt;@Localizer["Tenant.Select"]&gt;</option>
@ -121,13 +121,13 @@ else
<hr class="app-rule" />
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="name" HelpText="Enter the name for the tenant" ResourceKey="TenantName">Tenant Name: </Label>
<Label Class="col-sm-3" For="name" HelpText="Enter the name for the database" ResourceKey="TenantName">Name: </Label>
<div class="col-sm-9">
<input id="name" class="form-control" @bind="@_tenantName" maxlength="100" required />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="databaseType" HelpText="Select the database type for the tenant" ResourceKey="DatabaseType">Database Type: </Label>
<Label Class="col-sm-3" For="databaseType" HelpText="Select the database type" ResourceKey="DatabaseType">Type: </Label>
<div class="col-sm-9">
@if (_databases != null)
{
@ -160,7 +160,7 @@ else
else
{
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="connectionstring" HelpText="Enter a complete connection string including all parameters and delimiters" ResourceKey="ConnectionString">String:</Label>
<Label Class="col-sm-3" For="connectionstring" HelpText="Enter a complete connection string including all parameters and delimiters" ResourceKey="ConnectionString">Settings:</Label>
<div class="col-sm-9">
<textarea id="connectionstring" class="form-control" @bind="@_connectionString" rows="3"></textarea>
</div>
@ -315,7 +315,7 @@ else
_urls = Regex.Replace(_urls, @"\r\n?|\n", ",");
var duplicates = new List<string>();
var aliases = await AliasService.GetAliasesAsync();
foreach (string name in _urls.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
foreach (string name in _urls.Split(',', StringSplitOptions.RemoveEmptyEntries))
{
if (aliases.Exists(item => item.Name == name))
{
@ -329,7 +329,7 @@ else
if (_tenantid == "+")
{
if (!string.IsNullOrEmpty(_tenantName) && _tenants.FirstOrDefault(item => item.Name == _tenantName) == null)
if (!string.IsNullOrEmpty(_tenantName) && !_tenants.Exists(item => item.Name == _tenantName))
{
// validate host credentials
var user = new User();

View File

@ -1,6 +1,7 @@
@namespace Oqtane.Modules.Admin.Sql
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject ISystemService SystemService
@inject ITenantService TenantService
@inject IDatabaseService DatabaseService
@inject ISqlService SqlService
@ -14,29 +15,100 @@
else
{
<div class="container">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="tenant" HelpText="Select the tenant associated with the database server" ResourceKey="Tenant">Tenant: </Label>
<Label Class="col-sm-3" For="connection" HelpText="Select a database connection (from appsettings.json)" ResourceKey="Connection">Connection: </Label>
<div class="col-sm-9">
<select id="tenant" class="form-select" value="@_tenantid" @onchange="(e => TenantChanged(e))">
<option value="-1">&lt;@Localizer["Tenant.Select"]&gt;</option>
@foreach (Tenant tenant in _tenants)
<select id="tenant" class="form-select" value="@_connection" @onchange="(e => ConnectionChanged(e))">
<option value="-">&lt;@Localizer["Connection.Select"]&gt;</option>
<option value="+">&lt;@Localizer["Connection.Add"]&gt;</option>
@foreach (var connection in _connections)
{
<option value="@tenant.TenantId">@tenant.Name</option>
<option value="@connection.Key">@connection.Key</option>
}
</select>
</div>
</div>
@if (_tenantid != "-1")
@if (_connection == "+")
{
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="database" HelpText="The database for the tenant" ResourceKey="Database">Database: </Label>
<Label Class="col-sm-3" For="name" HelpText="Enter the name of the connection" ResourceKey="Name">Name: </Label>
<div class="col-sm-9">
<input id="database" class="form-control" @bind="@_database" readonly />
<input id="name" class="form-control" @bind="@_name" maxlength="100" required />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="connectionstring" HelpText="The connection information for the database" ResourceKey="ConnectionString">Connection: </Label>
<Label Class="col-sm-3" For="databasetype" HelpText="Select the database type" ResourceKey="DatabaseType">Type: </Label>
<div class="col-sm-9">
@if (_databases != null)
{
<div class="input-group">
<select id="databasetype" class="form-select" value="@_databasetype" @onchange="(e => DatabaseTypeChanged(e))" required>
@foreach (var database in _databases)
{
<option value="@database.Name">@Localizer[@database.Name]</option>
}
</select>
@if (!_showConnectionString)
{
<button type="button" class="btn btn-secondary" @onclick="ShowConnectionString">@Localizer["EnterConnectionString"]</button>
}
else
{
<button type="button" class="btn btn-secondary" @onclick="ShowConnectionString">@Localizer["EnterConnectionParameters"]</button>
}
</div>
}
</div>
</div>
@if (!_showConnectionString)
{
if (_databaseConfigType != null)
{
@DatabaseConfigComponent
}
}
else
{
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="connectionstring" HelpText="Enter a complete connection string including all parameters and delimiters" ResourceKey="ConnectionString">Settings:</Label>
<div class="col-sm-9">
<textarea id="connectionstring" class="form-control" @bind="@_connectionstring" rows="3"></textarea>
</div>
</div>
}
<br />
<button type="button" class="btn btn-success" @onclick="Add">@Localizer["Add"]</button>
}
else
{
@if (_connection != "-")
{
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="databasetype" HelpText="The database type" ResourceKey="DatabaseType">Type: </Label>
<div class="col-sm-9">
@if (_databases != null)
{
<select id="databasetype" class="form-select" @bind="@_databasetype" required>
<option value="-">&lt;@Localizer["Type.Select"]&gt;</option>
@foreach (var database in _databases)
{
<option value="@database.Name">@Localizer[@database.Name]</option>
}
</select>
}
</div>
</div>
@if (!string.IsNullOrEmpty(_tenant))
{
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="tenant" HelpText="The database using this connection" ResourceKey="Tenant">Database: </Label>
<div class="col-sm-9">
<input id="tenant" class="form-control" @bind="@_tenant" readonly />
</div>
</div>
}
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="connectionstring" HelpText="The connection string" ResourceKey="ConnectionString">Settings: </Label>
<div class="col-sm-9">
<div class="input-group">
<input id="connectionstring" type="@_connectionstringtype" class="form-control" @bind="@_connectionstring" readonly />
@ -45,13 +117,11 @@ else
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="sqlQuery" HelpText="Enter the SQL query for the database server" ResourceKey="SqlQuery">SQL Query: </Label>
<Label Class="col-sm-3" For="sqlQuery" HelpText="Enter a valid SQL query for the database" ResourceKey="SqlQuery">SQL Query: </Label>
<div class="col-sm-9">
<textarea id="sqlQuery" class="form-control" @bind="@_sql" rows="3"></textarea>
</div>
</div>
}
</div>
<br />
<button type="button" class="btn btn-success" @onclick="Execute">@Localizer["Execute"]</button>
<br />
@ -80,14 +150,27 @@ else
@Localizer["Return.NoResult"]
}
<br />
<br />
}
}
}
</div>
}
@code {
private string _connection = "-";
private Dictionary<string, object> _connections;
private List<Tenant> _tenants;
private string _tenantid = "-1";
private string _database = string.Empty;
private List<Database> _databases;
private string _name = string.Empty;
private string _databasetype = string.Empty;
private Type _databaseConfigType;
private object _databaseConfig;
private RenderFragment DatabaseConfigComponent { get; set; }
private bool _showConnectionString = false;
private string _tenant = string.Empty;
private string _connectionstring = string.Empty;
private string _connectionstringtype = "password";
private string _connectionstringtoggle = string.Empty;
@ -100,7 +183,9 @@ else
{
try
{
_connections = await SystemService.GetSystemInfoAsync("connectionstrings");
_tenants = await TenantService.GetTenantsAsync();
_databases = await DatabaseService.GetDatabasesAsync();
_connectionstringtoggle = SharedLocalizer["ShowPassword"];
}
catch (Exception ex)
@ -110,28 +195,105 @@ else
}
}
private async void TenantChanged(ChangeEventArgs e)
private async void ConnectionChanged(ChangeEventArgs e)
{
try
{
_tenantid = (string)e.Value;
var tenants = await TenantService.GetTenantsAsync();
var _databases = await DatabaseService.GetDatabasesAsync();
var tenant = tenants.Find(item => item.TenantId == int.Parse(_tenantid));
_connection = (string)e.Value;
if (_connection != "-" && _connection != "+")
{
_connectionstring = _connections[_connection].ToString();
_tenant = "";
_databasetype = "-";
var tenant = _tenants.FirstOrDefault(item => item.DBConnectionString == _connection);
if (tenant != null)
{
_database = _databases.Find(item => item.DBType == tenant.DBType)?.Name;
_connectionstring = tenant.DBConnectionString;
_tenant = tenant.Name;
_databasetype = _databases.FirstOrDefault(item => item.DBType == tenant.DBType).Name;
}
}
else
{
if (_databases.Exists(item => item.IsDefault))
{
_databasetype = _databases.Find(item => item.IsDefault).Name;
}
else
{
_databasetype = "LocalDB";
}
_showConnectionString = false;
LoadDatabaseConfigComponent();
}
StateHasChanged();
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Loading Tenant {TenantId} {Error}", _tenantid, ex.Message);
await logger.LogError(ex, "Error Loading Connection {Connection} {Error}", _connection, ex.Message);
AddModuleMessage(ex.Message, MessageType.Error);
}
}
private void DatabaseTypeChanged(ChangeEventArgs eventArgs)
{
try
{
_databasetype = (string)eventArgs.Value;
_showConnectionString = false;
LoadDatabaseConfigComponent();
}
catch
{
AddModuleMessage(Localizer["Error.Database.LoadConfig"], MessageType.Error);
}
}
private void LoadDatabaseConfigComponent()
{
var database = _databases.SingleOrDefault(d => d.Name == _databasetype);
if (database != null)
{
_databaseConfigType = Type.GetType(database.ControlType);
DatabaseConfigComponent = builder =>
{
builder.OpenComponent(0, _databaseConfigType);
builder.AddComponentReferenceCapture(1, inst => { _databaseConfig = Convert.ChangeType(inst, _databaseConfigType); });
builder.CloseComponent();
};
}
}
private void ShowConnectionString()
{
if (_databaseConfig is IDatabaseConfigControl databaseConfigControl)
{
_connectionstring = databaseConfigControl.GetConnectionString();
}
_showConnectionString = !_showConnectionString;
}
private async Task Add()
{
var connectionstring = _connectionstring;
if (!_showConnectionString && _databaseConfig is IDatabaseConfigControl databaseConfigControl)
{
connectionstring = databaseConfigControl.GetConnectionString();
}
if (!string.IsNullOrEmpty(_name) && !string.IsNullOrEmpty(connectionstring))
{
var settings = new Dictionary<string, object>();
settings.Add($"{SettingKeys.ConnectionStringsSection}:{_name}", connectionstring);
await SystemService.UpdateSystemInfoAsync(settings);
_connections = await SystemService.GetSystemInfoAsync("connectionstrings");
_connection = "-";
AddModuleMessage(Localizer["Message.Connection.Added"], MessageType.Success);
}
else
{
AddModuleMessage(Localizer["Message.Required.Connection"], MessageType.Warning);
}
}
private void ToggleConnectionString()
{
if (_connectionstringtype == "password")
@ -150,9 +312,10 @@ else
{
try
{
if (_tenantid != "-1" && !string.IsNullOrEmpty(_sql))
if (_databasetype != "-" && !string.IsNullOrEmpty(_sql))
{
var sqlquery = new SqlQuery { TenantId = int.Parse(_tenantid), Query = _sql };
var dbtype = _databases.FirstOrDefault(item => item.Name == _databasetype).DBType;
var sqlquery = new SqlQuery { DBConnectionString = _connection, DBType = dbtype, Query = _sql };
sqlquery = await SqlService.ExecuteQueryAsync(sqlquery);
_results = sqlquery.Results;
AddModuleMessage(Localizer["Success.QueryExecuted"], MessageType.Success);

View File

@ -156,6 +156,8 @@
</div>
</div>
</div>
<br /><br />
<button type="button" class="btn btn-danger" @onclick="ClearLog">@Localizer["Clear"]</button>
</TabPanel>
</TabStrip>
<br /><br />
@ -188,7 +190,7 @@
{
_version = Constants.Version;
Dictionary<string, object> systeminfo = await SystemService.GetSystemInfoAsync("environment");
var systeminfo = await SystemService.GetSystemInfoAsync("environment");
if (systeminfo != null)
{
_clrversion = systeminfo["CLRVersion"].ToString();
@ -241,6 +243,23 @@
}
}
private async Task ClearLog()
{
try
{
var settings = new Dictionary<string, object>();
settings.Add("clearlog", "true");
await SystemService.UpdateSystemInfoAsync(settings);
_log = string.Empty;
AddModuleMessage(Localizer["Success.ClearLog"], MessageType.Success);
}
catch (Exception ex)
{
await logger.LogError(ex, "Error Clearing Log");
AddModuleMessage(Localizer["Error.ClearLog"], MessageType.Error);
}
}
private async Task RestartApplication()
{
try

View File

@ -121,7 +121,7 @@
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() != "theme" && Regex.IsMatch(name, "^[A-Za-z_][A-Za-z0-9_]*$");
return !string.IsNullOrEmpty(name) && name.ToLower() != "theme" && !name.ToLower().Contains("oqtane") && Regex.IsMatch(name, "^[A-Za-z_][A-Za-z0-9_]*$");
}
private void TemplateChanged(ChangeEventArgs e)

View File

@ -211,8 +211,11 @@ else
</Detail>
</Pager>
}
@if (notifications.Any())
{
<br />
<ActionDialog Header="Clear Notifications" Message="Are You Sure You Wish To Permanently Delete All Notifications ?" Action="Delete All Notifications" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteAllNotifications())" ResourceKey="DeleteAllNotifications" />
}
<br /><hr />
<select class="form-select" @onchange="(e => FilterChanged(e))">
<option value="to">@Localizer["Inbox"]</option>

View File

@ -294,6 +294,12 @@ else
<input id="roleclaimtype" class="form-control" @bind="@_roleclaimtype" />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="profileclaimtypes" HelpText="A comma delimited list of user profile claims provided by the provider, as well as mappings to your user profile definition. For example if the provider includes a 'given_name' claim and you have a 'FirstName' user profile definition you should specify 'given_name:FirstName'." ResourceKey="ProfileClaimTypes">User Profile Claims:</Label>
<div class="col-sm-9">
<input id="profileclaimtypes" class="form-control" @bind="@_profileclaimtypes" />
</div>
</div>
}
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="domainfilter" HelpText="Provide any email domain filter criteria (separated by commas). Domains to exclude should be prefixed with an exclamation point (!). For example 'microsoft.com,!hotmail.com' would include microsoft.com email addresses but not hotmail.com email addresses." ResourceKey="DomainFilter">Domain Filter:</Label>
@ -395,6 +401,7 @@ else
private string _identifierclaimtype;
private string _emailclaimtype;
private string _roleclaimtype;
private string _profileclaimtypes;
private string _domainfilter;
private string _createusers;
@ -449,6 +456,7 @@ else
_identifierclaimtype = SettingService.GetSetting(settings, "ExternalLogin:IdentifierClaimType", "sub");
_emailclaimtype = SettingService.GetSetting(settings, "ExternalLogin:EmailClaimType", "email");
_roleclaimtype = SettingService.GetSetting(settings, "ExternalLogin:RoleClaimType", "");
_profileclaimtypes = SettingService.GetSetting(settings, "ExternalLogin:ProfileClaimTypes", "");
_domainfilter = SettingService.GetSetting(settings, "ExternalLogin:DomainFilter", "");
_createusers = SettingService.GetSetting(settings, "ExternalLogin:CreateUsers", "true");
@ -568,6 +576,7 @@ else
settings = SettingService.SetSetting(settings, "ExternalLogin:IdentifierClaimType", _identifierclaimtype, true);
settings = SettingService.SetSetting(settings, "ExternalLogin:EmailClaimType", _emailclaimtype, true);
settings = SettingService.SetSetting(settings, "ExternalLogin:RoleClaimType", _roleclaimtype, true);
settings = SettingService.SetSetting(settings, "ExternalLogin:ProfileClaimTypes", _profileclaimtypes, true);
settings = SettingService.SetSetting(settings, "ExternalLogin:DomainFilter", _domainfilter, true);
settings = SettingService.SetSetting(settings, "ExternalLogin:CreateUsers", _createusers, true);

View File

@ -1,4 +1,5 @@
@namespace Oqtane.Modules.Controls
@using System.Text.Json
@inherits LocalizableComponent
@if (_visible)
@ -40,7 +41,7 @@
@code {
private bool _visible = false;
private string _permissions = string.Empty;
private List<Permission> _permissions;
private bool _editmode = false;
private bool _authorized = false;
private string _iconSpan = string.Empty;
@ -61,7 +62,10 @@
public SecurityAccessLevel? Security { get; set; } // optional - can be used to explicitly specify SecurityAccessLevel
[Parameter]
public string Permissions { get; set; } // optional - can be used to specify a permission string
public string Permissions { get; set; } // deprecated - use PermissionList instead
[Parameter]
public List<Permission> PermissionList { get; set; } // optional - can be used to specify permissions
[Parameter]
public string Class { get; set; } // optional
@ -78,6 +82,14 @@
[Parameter]
public string IconName { get; set; } // optional - specifies an icon for the link - default is no icon
protected override void OnInitialized()
{
if (!string.IsNullOrEmpty(Permissions))
{
PermissionList = JsonSerializer.Deserialize<List<Permission>>(Permissions);
}
}
protected override void OnParametersSet()
{
base.OnParametersSet();
@ -109,7 +121,7 @@
Header = Localize(nameof(Header), Header);
Message = Localize(nameof(Message), Message);
_permissions = (string.IsNullOrEmpty(Permissions)) ? ModuleState.Permissions : Permissions;
_permissions = (PermissionList == null) ? ModuleState.PermissionList : PermissionList;
_authorized = IsAuthorized();
}

View File

@ -1,5 +1,6 @@
@namespace Oqtane.Modules.Controls
@using System.Net
@using System.Text.Json
@inherits LocalizableComponent
@inject IUserService UserService
@ -26,7 +27,7 @@
private string _text = string.Empty;
private string _parameters = string.Empty;
private string _url = string.Empty;
private string _permissions = string.Empty;
private List<Permission> _permissions;
private bool _editmode = false;
private bool _authorized = false;
private string _classname = "btn btn-primary";
@ -52,7 +53,10 @@
public SecurityAccessLevel? Security { get; set; } // optional - can be used to explicitly specify SecurityAccessLevel
[Parameter]
public string Permissions { get; set; } // optional - can be used to specify a permission string
public string Permissions { get; set; } // deprecated - use PermissionList instead
[Parameter]
public List<Permission> PermissionList { get; set; } // optional - can be used to specify permissions
[Parameter]
public bool Disabled { get; set; } // optional
@ -75,6 +79,14 @@
[Parameter]
public string ReturnUrl { get; set; } // optional - used to set a url to redirect to
protected override void OnInitialized()
{
if (!string.IsNullOrEmpty(Permissions))
{
PermissionList = JsonSerializer.Deserialize<List<Permission>>(Permissions);
}
}
protected override void OnParametersSet()
{
base.OnParametersSet();
@ -119,7 +131,7 @@
_iconSpan = $"<span class=\"{IconName}\"></span>{(IconOnly ? "" : "&nbsp")}";
}
_permissions = (string.IsNullOrEmpty(Permissions)) ? ModuleState.Permissions : Permissions;
_permissions = (PermissionList == null) ? ModuleState.PermissionList : PermissionList;
_text = Localize(nameof(Text), _text);
_url = (ModuleId == -1) ? EditUrl(Action, _parameters) : EditUrl(ModuleId, Action, _parameters);
if (!string.IsNullOrEmpty(ReturnUrl))

View File

@ -6,7 +6,7 @@
@inject IStringLocalizer<FileManager> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer
@if (_folders != null)
@if (_initialized)
{
<div id="@Id" class="container-fluid px-0">
<div class="row">
@ -87,6 +87,7 @@
}
@code {
private bool _initialized = false;
private List<Folder> _folders;
private List<File> _files = new List<File>();
private string _fileinputid = string.Empty;
@ -205,6 +206,8 @@
_fileinputid = "FileInput_" + _guid;
_progressinfoid = "ProgressInfo_" + _guid;
_progressbarid = "ProgressBar_" + _guid;
_initialized = true;
}
private async Task GetFiles()
@ -220,7 +223,7 @@
Folder folder = _folders.FirstOrDefault(item => item.FolderId == FolderId);
if (folder != null)
{
_haseditpermission = UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, folder.Permissions);
_haseditpermission = UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, folder.PermissionList);
_files = await FileService.GetFilesAsync(FolderId);
}
else

View File

@ -1,4 +1,5 @@
@namespace Oqtane.Modules.Controls
@using System.Text.Json
@inherits ModuleControlBase
@inject IRoleService RoleService
@inject IUserService UserService
@ -15,20 +16,19 @@
<tbody>
<tr>
<th scope="col">@Localizer["Role"]</th>
@foreach (PermissionString permission in _permissions)
@foreach (var permissionname in _permissionnames)
{
<th style="text-align: center; width: 1px;">@((MarkupString)GetPermissionName(permission).Replace(" ", "<br />"))</th>
<th style="text-align: center; width: 1px;">@((MarkupString)DisplayPermissionName(permissionname).Replace(" ", "<br />"))</th>
}
</tr>
@foreach (Role role in _roles)
{
<tr>
<td>@role.Name</td>
@foreach (PermissionString permission in _permissions)
@foreach (var permissionname in _permissionnames)
{
var p = permission;
<td style="text-align: center;">
<TriStateCheckBox Value=@GetPermissionValue(p.Permissions, role.Name) Disabled="@GetPermissionDisabled(p.EntityName, p.PermissionName, role.Name)" OnChange="@(e => PermissionChanged(e, p.EntityName, p.PermissionName, role.Name))" />
<TriStateCheckBox Value=@GetPermissionValue(permissionname, role.Name, -1) Disabled="@GetPermissionDisabled(permissionname, role.Name)" OnChange="@(e => PermissionChanged(e, permissionname, role.Name, -1))" />
</td>
}
</tr>
@ -50,23 +50,21 @@
<thead>
<tr>
<th scope="col">@Localizer["User"]</th>
@foreach (PermissionString permission in _permissions)
@foreach (var permissionname in _permissionnames)
{
<th style="text-align: center; width: 1px;">@Localizer[permission.PermissionName]</th>
<th style="text-align: center; width: 1px;">@((MarkupString)DisplayPermissionName(permissionname).Replace(" ", "<br />"))</th>
}
</tr>
</thead>
<tbody>
@foreach (User user in _users)
{
string userid = "[" + user.UserId.ToString() + "]";
<tr>
<td>@user.DisplayName</td>
@foreach (PermissionString permission in _permissions)
@foreach (var permissionname in _permissionnames)
{
var p = permission;
<td style="text-align: center; width: 1px;">
<TriStateCheckBox Value=@GetPermissionValue(p.Permissions, userid) Disabled="@GetPermissionDisabled(p.EntityName, p.PermissionName, "")" OnChange="@(e => PermissionChanged(e, p.EntityName, p.PermissionName, userid))" />
<TriStateCheckBox Value=@GetPermissionValue(permissionname, "", user.UserId) Disabled="@GetPermissionDisabled(permissionname, "")" OnChange="@(e => PermissionChanged(e, permissionname, "", user.UserId))" />
</td>
}
</tr>
@ -94,9 +92,9 @@
}
@code {
private string _permissionnames = string.Empty;
private List<string> _permissionnames;
private List<Permission> _permissions;
private List<Role> _roles;
private List<PermissionString> _permissions;
private List<User> _users = new List<User>();
private AutoComplete _user;
private string _message = string.Empty;
@ -108,17 +106,16 @@
public string PermissionNames { get; set; }
[Parameter]
public string Permissions { get; set; }
public string Permissions { get; set; } // deprecated - use PermissionList instead
[Parameter]
public List<Permission> PermissionList { get; set; }
protected override async Task OnInitializedAsync()
{
if (string.IsNullOrEmpty(PermissionNames))
if (!string.IsNullOrEmpty(Permissions))
{
_permissionnames = Shared.PermissionNames.View + "," + Shared.PermissionNames.Edit;
}
else
{
_permissionnames = PermissionNames;
PermissionList = JsonSerializer.Deserialize<List<Permission>>(Permissions);
}
_roles = await RoleService.GetRolesAsync(ModuleState.SiteId, true);
@ -127,9 +124,37 @@
_roles.RemoveAll(item => item.Name == RoleNames.Host);
}
_permissions = new List<PermissionString>();
// get permission names
if (string.IsNullOrEmpty(PermissionNames))
{
_permissionnames = new List<string>();
_permissionnames.Add(Shared.PermissionNames.View);
_permissionnames.Add(Shared.PermissionNames.Edit);
}
else
{
_permissionnames = PermissionNames.Split(',', StringSplitOptions.RemoveEmptyEntries).ToList();
}
foreach (string permissionname in _permissionnames.Split(',', StringSplitOptions.RemoveEmptyEntries))
// initialize permissions
_permissions = new List<Permission>();
if (PermissionList != null && PermissionList.Any())
{
foreach (var permission in PermissionList)
{
_permissions.Add(permission);
if (permission.UserId != null)
{
if (!_users.Any(item => item.UserId == permission.UserId.Value))
{
_users.Add(await UserService.GetUserAsync(permission.UserId.Value, ModuleState.SiteId));
}
}
}
}
else
{
foreach (string permissionname in _permissionnames)
{
// permission names can be in the form of "EntityName:PermissionName:Roles"
if (permissionname.Contains(":"))
@ -137,78 +162,65 @@
var segments = permissionname.Split(':');
if (segments.Length == 3)
{
if (!segments[2].Contains(RoleNames.Admin))
foreach (var role in segments[2].Split(';'))
{
segments[2] = RoleNames.Admin + ";" + segments[2]; // ensure admin access
_permissions.Add(new Permission(ModuleState.SiteId, segments[0], segments[1], role, null, true));
}
// ensure admin access
if (!_permissions.Any(item => item.EntityName == segments[0] && item.PermissionName == segments[1] && item.RoleName == RoleNames.Admin))
{
_permissions.Add(new Permission(ModuleState.SiteId, segments[0], segments[1], RoleNames.Admin, null, true));
}
_permissions.Add(new PermissionString { EntityName = segments[0], PermissionName = segments[1], Permissions = segments[2] });
}
}
else
{
_permissions.Add(new PermissionString { EntityName = EntityName, PermissionName = permissionname, Permissions = RoleNames.Admin });
}
}
if (!string.IsNullOrEmpty(Permissions))
{
// populate permissions
foreach (PermissionString permissionstring in UserSecurity.GetPermissionStrings(Permissions))
{
int index = _permissions.FindIndex(item => item.EntityName == permissionstring.EntityName && item.PermissionName == permissionstring.PermissionName);
if (index != -1)
{
_permissions[index].Permissions = permissionstring.Permissions;
}
if (permissionstring.Permissions.Contains("["))
{
foreach (string user in permissionstring.Permissions.Split('[', StringSplitOptions.RemoveEmptyEntries))
{
if (user.Contains("]"))
{
var userid = int.Parse(user.Substring(0, user.IndexOf("]")));
if (_users.Where(item => item.UserId == userid).FirstOrDefault() == null)
{
_users.Add(await UserService.GetUserAsync(userid, ModuleState.SiteId));
}
}
}
_permissions.Add(new Permission(ModuleState.SiteId, EntityName, permissionname, RoleNames.Admin, null, true));
}
}
}
}
private string GetPermissionName(PermissionString permission)
private string GetPermissionName(string permissionName)
{
var permissionname = Localizer[permission.PermissionName].ToString();
if (!string.IsNullOrEmpty(EntityName))
{
permissionname += " " + Localizer[permission.EntityName].ToString();
}
return permissionname;
return (permissionName.Contains(":")) ? permissionName.Split(':')[1] : permissionName;
}
private bool? GetPermissionValue(string permissions, string securityKey)
private string GetEntityName(string permissionName)
{
if ((";" + permissions + ";").Contains(";" + "!" + securityKey + ";"))
return (permissionName.Contains(":")) ? permissionName.Split(':')[0] : EntityName;
}
private string DisplayPermissionName(string permissionName)
{
return false; // deny permission
var name = Localizer[GetPermissionName(permissionName)].ToString();
name += " " + Localizer[GetEntityName(permissionName)].ToString();
return name;
}
private bool? GetPermissionValue(string permissionName, string roleName, int userId)
{
bool? isauthorized = null;
if (roleName != "")
{
var permission = _permissions.FirstOrDefault(item => item.EntityName == GetEntityName(permissionName) && item.PermissionName == GetPermissionName(permissionName) && item.RoleName == roleName);
if (permission != null)
{
isauthorized = permission.IsAuthorized;
}
}
else
{
if ((";" + permissions + ";").Contains(";" + securityKey + ";"))
var permission = _permissions.FirstOrDefault(item => item.EntityName == GetEntityName(permissionName) && item.PermissionName == GetPermissionName(permissionName) && item.UserId == userId);
if (permission != null)
{
return true; // grant permission
}
else
{
return null; // not specified
isauthorized = permission.IsAuthorized;
}
}
return isauthorized;
}
private bool GetPermissionDisabled(string entityName, string permissionName, string roleName)
private bool GetPermissionDisabled(string permissionName, string roleName)
{
if (roleName == RoleNames.Admin && !UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
{
@ -216,7 +228,7 @@
}
else
{
if (entityName != EntityName && !UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
if (GetEntityName(permissionName) != EntityName && !UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
{
return true;
}
@ -227,6 +239,34 @@
}
}
private void PermissionChanged(bool? value, string permissionName, string roleName, int userId)
{
if (roleName != "")
{
var permission = _permissions.FirstOrDefault(item => item.EntityName == GetEntityName(permissionName) && item.PermissionName == GetPermissionName(permissionName) && item.RoleName == roleName);
if (permission != null)
{
_permissions.Remove(permission);
}
if (value != null)
{
_permissions.Add(new Permission(ModuleState.SiteId, GetEntityName(permissionName), GetPermissionName(permissionName), roleName, null, value.Value));
}
}
else
{
var permission = _permissions.FirstOrDefault(item => item.EntityName == GetEntityName(permissionName) && item.PermissionName == GetPermissionName(permissionName) && item.UserId == userId);
if (permission != null)
{
_permissions.Remove(permission);
}
if (value != null)
{
_permissions.Add(new Permission(ModuleState.SiteId, GetEntityName(permissionName), GetPermissionName(permissionName), null, userId, value.Value));
}
}
}
private async Task<Dictionary<string, string>> GetUsers(string filter)
{
var users = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId, RoleNames.Registered);
@ -251,62 +291,45 @@
_user.Clear();
}
private void PermissionChanged(bool? value, string entityName, string permissionName, string securityId)
{
var selected = value;
int index = _permissions.FindIndex(item => item.EntityName == entityName && item.PermissionName == permissionName);
if (index != -1)
{
var permission = _permissions[index];
var ids = permission.Permissions.Split(';').ToList();
ids.Remove(securityId); // remove grant permission
ids.Remove("!" + securityId); // remove deny permission
switch (selected)
{
case true:
ids.Add(securityId); // add grant permission
break;
case false:
ids.Add("!" + securityId); // add deny permission
break;
case null:
break; // permission not specified
}
_permissions[index].Permissions = string.Join(";", ids.ToArray());
}
}
public string GetPermissions()
{
ValidatePermissions();
return UserSecurity.SetPermissionStrings(_permissions);
return JsonSerializer.Serialize(_permissions);
}
public List<Permission> GetPermissionList()
{
ValidatePermissions();
return _permissions;
}
private void ValidatePermissions()
{
PermissionString permission;
for (int index = 0; index < _permissions.Count; index++)
// remove deny all users, unauthenticated, and registered users
var permissions = _permissions.Where(item => !item.IsAuthorized &&
(item.RoleName == RoleNames.Everyone || item.RoleName == RoleNames.Unauthenticated || item.RoleName == RoleNames.Registered)).ToList();
foreach (var permission in permissions)
{
permission = _permissions[index];
List<string> ids = permission.Permissions.Split(';', StringSplitOptions.RemoveEmptyEntries).ToList();
ids.Remove("!" + RoleNames.Everyone); // remove deny all users
ids.Remove("!" + RoleNames.Unauthenticated); // remove deny unauthenticated
ids.Remove("!" + RoleNames.Registered); // remove deny registered users
_permissions.Remove(permission);
}
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
{
ids.Remove("!" + RoleNames.Admin); // remove deny administrators
ids.Remove("!" + RoleNames.Host); // remove deny host users
if (!ids.Contains(RoleNames.Host) && !ids.Contains(RoleNames.Admin))
// remove deny administrators and host users
permissions = _permissions.Where(item => !item.IsAuthorized &&
(item.RoleName == RoleNames.Admin || item.RoleName == RoleNames.Host)).ToList();
foreach (var permission in permissions)
{
// add administrators role if host user role is not assigned
ids.Add(RoleNames.Admin);
_permissions.Remove(permission);
}
foreach (var permissionname in _permissionnames)
{
// add administrators role if neither host or administrator is assigned
if (!_permissions.Any(item => item.EntityName == GetEntityName(permissionname) && item.PermissionName == GetPermissionName(permissionname) &&
(item.RoleName == RoleNames.Admin || item.RoleName == RoleNames.Host)))
{
_permissions.Add(new Permission(ModuleState.SiteId, GetEntityName(permissionname), GetPermissionName(permissionname), RoleNames.Admin, null, true));
}
}
permission.Permissions = string.Join(";", ids.ToArray());
_permissions[index] = permission;
}
}
}

View File

@ -93,10 +93,10 @@
authorized = true;
break;
case SecurityAccessLevel.View:
authorized = UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, ModuleState.Permissions);
authorized = UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, ModuleState.PermissionList);
break;
case SecurityAccessLevel.Edit:
authorized = UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, ModuleState.Permissions);
authorized = UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, ModuleState.PermissionList);
break;
case SecurityAccessLevel.Admin:
authorized = UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin);

View File

@ -190,12 +190,7 @@ namespace Oqtane.Modules
public string AddUrlParameters(params object[] parameters)
{
var url = "";
for (var i = 0; i < parameters.Length; i++)
{
url += "/" + parameters[i].ToString();
}
return url;
return Utilities.AddUrlParameters(parameters);
}
// template is in the form of a standard route template ie. "/{id}/{name}" and produces dictionary of key/value pairs

View File

@ -5,7 +5,7 @@
<OutputType>Exe</OutputType>
<RazorLangVersion>3.0</RazorLangVersion>
<Configurations>Debug;Release</Configurations>
<Version>3.3.1</Version>
<Version>3.4.2</Version>
<Product>Oqtane</Product>
<Authors>Shaun Walker</Authors>
<Company>.NET Foundation</Company>
@ -13,7 +13,7 @@
<Copyright>.NET Foundation</Copyright>
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.3.1</PackageReleaseNotes>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.4.2</PackageReleaseNotes>
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
<RootNamespace>Oqtane</RootNamespace>

View File

@ -198,6 +198,8 @@ namespace Oqtane.Client
private static void RegisterModuleServices(Assembly assembly, IServiceCollection services)
{
// dynamically register module scoped services
try
{
var implementationTypes = assembly.GetInterfaces<IService>();
foreach (var implementationType in implementationTypes)
{
@ -208,8 +210,15 @@ namespace Oqtane.Client
}
}
}
catch
{
// could not interrogate assembly - likely missing dependencies
}
}
private static void RegisterClientStartups(Assembly assembly, IServiceCollection services)
{
try
{
var startUps = assembly.GetInstances<IClientStartup>();
foreach (var startup in startUps)
@ -217,6 +226,11 @@ namespace Oqtane.Client
startup.ConfigureServices(services);
}
}
catch
{
// could not interrogate assembly - likely missing dependencies
}
}
private static async Task SetCultureFromLocalizationCookie(IServiceProvider serviceProvider)
{

View File

@ -172,7 +172,7 @@
<value>Enter a complete connection string including all parameters and delimiters</value>
</data>
<data name="ConnectionString.Text" xml:space="preserve">
<value>String:</value>
<value>Settings:</value>
</data>
<data name="EnterConnectionParameters" xml:space="preserve">
<value>Enter Connection Parameters</value>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
@ -193,6 +193,6 @@
<value>Execute Once</value>
</data>
<data name="Message.NoJobs" xml:space="preserve">
<value>Please Note That After An Initial Installation You Must &amp;lt;a href={0}&amp;gt;Restart&amp;lt;/a&amp;gt; The Application In Order To Activate The Default Scheduled Jobs.</value>
<value>Please Note That After An Initial Installation You Must &lt;a href={0}&gt;Restart&lt;/a&gt; The Application In Order To Activate The Default Scheduled Jobs.</value>
</data>
</root>

View File

@ -120,18 +120,12 @@
<data name="Error.Language.Add" xml:space="preserve">
<value>Error Adding Language</value>
</data>
<data name="Translated.HelpText" xml:space="preserve">
<value>Specify If You Wish To Select Languages That Have Translations Installed</value>
</data>
<data name="Name.HelpText" xml:space="preserve">
<value>Name Of The Langauage</value>
</data>
<data name="IsDefault.HelpText" xml:space="preserve">
<value>Indicates Whether Or Not This Language Is The Default For The Site</value>
</data>
<data name="Translated.Text" xml:space="preserve">
<value>Translated?</value>
</data>
<data name="Name.Text" xml:space="preserve">
<value>Name:</value>
</data>

View File

@ -132,6 +132,9 @@
<data name="DeleteLanguage.Header" xml:space="preserve">
<value>Delete Language</value>
</data>
<data name="Success.Language.Download" xml:space="preserve">
<value>Translation Downloaded Successfully. Click Install To Complete Installation.</value>
</data>
<data name="Error.Language.Download" xml:space="preserve">
<value>Error Downloading Translation</value>
</data>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
@ -166,7 +166,7 @@
<value>Email:</value>
</data>
<data name="Password.HelpText" xml:space="preserve">
<value>Please choose a sufficiently secure password and enter it here</value>
<value>Please enter a sufficiently secure password which meets the password complexity requirements</value>
</data>
<data name="Password.Text" xml:space="preserve">
<value>Password:</value>
@ -177,4 +177,19 @@
<data name="Username.Text" xml:space="preserve">
<value>Username:</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>
<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>

View File

@ -163,7 +163,7 @@
<value>Enter the site name</value>
</data>
<data name="Tenant.HelpText" xml:space="preserve">
<value>Enter the tenant for the site</value>
<value>The name of the database used for the site</value>
</data>
<data name="Aliases.HelpText" xml:space="preserve">
<value>The aliases for the site. An alias can be a domain name (www.site.com) or a virtual folder (ie. www.site.com/folder).</value>
@ -214,7 +214,7 @@
<value>Include a splash icon for your PWA. It should be a PNG which is 512 X 512 pixels in dimension.</value>
</data>
<data name="Tenant.Text" xml:space="preserve">
<value>Tenant: </value>
<value>Database: </value>
</data>
<data name="Aliases.Text" xml:space="preserve">
<value>Aliases: </value>
@ -292,7 +292,7 @@
<value>Browse</value>
</data>
<data name="TenantInformation.Heading" xml:space="preserve">
<value>Tenant Information</value>
<value>Database</value>
</data>
<data name="PWASettings.Heading" xml:space="preserve">
<value>PWA Settings</value>
@ -304,13 +304,13 @@
<value>Connection:</value>
</data>
<data name="Database.Text" xml:space="preserve">
<value>Database:</value>
<value>Type:</value>
</data>
<data name="ConnectionString.HelpText" xml:space="preserve">
<value>The connection information for the database</value>
</data>
<data name="Database.HelpText" xml:space="preserve">
<value>The database for the tenant</value>
<value>The type of database</value>
</data>
<data name="DeleteSite.Text" xml:space="preserve">
<value>Delete Site</value>
@ -345,4 +345,10 @@
<data name="SmtpRelay.Text" xml:space="preserve">
<value>Relay Configured?</value>
</data>
<data name="SiteMap.HelpText" xml:space="preserve">
<value>The site map url for this site which can be submitted to search engines for indexing</value>
</data>
<data name="SiteMap.Text" xml:space="preserve">
<value>Site Map:</value>
</data>
</root>

View File

@ -123,9 +123,6 @@
<data name="SqlServer" xml:space="preserve">
<value>SQL Server</value>
</data>
<data name="Server.Text" xml:space="preserve">
<value>Server: </value>
</data>
<data name="Container.Select" xml:space="preserve">
<value>Select Container</value>
</data>
@ -145,7 +142,7 @@
<value>Select the default container for the site</value>
</data>
<data name="Tenant.Text" xml:space="preserve">
<value>Tenant: </value>
<value>Database: </value>
</data>
<data name="Aliases.Text" xml:space="preserve">
<value>Aliases: </value>
@ -157,10 +154,10 @@
<value>Select Site Template</value>
</data>
<data name="Tenant.Select" xml:space="preserve">
<value>Select Tenant</value>
<value>Select Database</value>
</data>
<data name="Tenant.Add" xml:space="preserve">
<value>Create New Tenant</value>
<value>Create Database</value>
</data>
<data name="Error.Theme.LoadContainers" xml:space="preserve">
<value>Error Loading Containers For Theme</value>
@ -172,19 +169,19 @@
<value>Invalid Host Password</value>
</data>
<data name="Error.TenantName.Exists" xml:space="preserve">
<value>Tenant Name Is Missing Or Already Exists</value>
<value>Database Name Is Missing Or Already Exists</value>
</data>
<data name="Message.SiteName.InUse" xml:space="preserve">
<value>{0} Already Used For Another Site</value>
</data>
<data name="Message.Required.Tenant" xml:space="preserve">
<value>You Must Provide A Tenant, Site Name, Alias, Default Theme/Container, And Site Template</value>
<value>You Must Provide A Database, Site Name, Alias, Default Theme/Container, And Site Template</value>
</data>
<data name="Name.HelpText" xml:space="preserve">
<value>Enter the name of the site</value>
</data>
<data name="DefaultTheme.HelpText" xml:space="preserve">
<value>Select the default theme for the website</value>
<value>Select the default theme for the site</value>
</data>
<data name="AdminContainer.HelpText" xml:space="preserve">
<value>Select the admin container for the site</value>
@ -193,28 +190,13 @@
<value>Select the site template</value>
</data>
<data name="Tenant.HelpText" xml:space="preserve">
<value>Select the tenant for the site</value>
<value>Select the database for the site</value>
</data>
<data name="TenantName.HelpText" xml:space="preserve">
<value>Enter the name for the tenant</value>
<value>Enter the name for the database</value>
</data>
<data name="DatabaseType.HelpText" xml:space="preserve">
<value>Select the database type for the tenant</value>
</data>
<data name="DatabaseServer.HelpText" xml:space="preserve">
<value>Enter the server for the tenant</value>
</data>
<data name="Database.HelpText" xml:space="preserve">
<value>Enter the database for the tenant</value>
</data>
<data name="IntegratedSecurity.HelpText" xml:space="preserve">
<value>Select if you want integrated security or not</value>
</data>
<data name="DatabaseUsername.HelpText" xml:space="preserve">
<value>Enter the username for the integrated security</value>
</data>
<data name="DatabasePassword.HelpText" xml:space="preserve">
<value>Enter the password for the integrated security</value>
<value>Select the database type</value>
</data>
<data name="HostUsername.HelpText" xml:space="preserve">
<value>Enter the username of an existing host user</value>
@ -232,23 +214,14 @@
<value>Site Template: </value>
</data>
<data name="TenantName.Text" xml:space="preserve">
<value>Tenant Name: </value>
<value>Name: </value>
</data>
<data name="DatabaseType.Text" xml:space="preserve">
<value>Database Type: </value>
<value>Type: </value>
</data>
<data name="Database.Text" xml:space="preserve">
<value>Database: </value>
</data>
<data name="IntegratedSecurity.Text" xml:space="preserve">
<value>Integrated Security: </value>
</data>
<data name="DatabaseUsername.Text" xml:space="preserve">
<value>Database Username: </value>
</data>
<data name="DatabasePassword.Text" xml:space="preserve">
<value>Database Password: </value>
</data>
<data name="HostUsername.Text" xml:space="preserve">
<value>Host Username:</value>
</data>
@ -274,7 +247,7 @@
<value>Enter a complete connection string including all parameters and delimiters</value>
</data>
<data name="ConnectionString.Text" xml:space="preserve">
<value>String:</value>
<value>Settings:</value>
</data>
<data name="EnterConnectionParameters" xml:space="preserve">
<value>Enter Connection Parameters</value>

View File

@ -117,30 +117,75 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Tenant.Text" xml:space="preserve">
<value>Tenant: </value>
<data name="Connection.Text" xml:space="preserve">
<value>Connection: </value>
</data>
<data name="Tenant.Select" xml:space="preserve">
<value>Select Tenant</value>
<data name="Connection.HelpText" xml:space="preserve">
<value>Select a database connection (from appsettings.json)</value>
</data>
<data name="Connection.Select" xml:space="preserve">
<value>Select Connection</value>
</data>
<data name="Connection.Add" xml:space="preserve">
<value>Add Connection</value>
</data>
<data name="Name.Text" xml:space="preserve">
<value>Name: </value>
</data>
<data name="Name.HelpText" xml:space="preserve">
<value>Enter the name of the connection</value>
</data>
<data name="DatabaseType.Text" xml:space="preserve">
<value>Type: </value>
</data>
<data name="DatabaseType.HelpText" xml:space="preserve">
<value>Select the database type</value>
</data>
<data name="Type.Select" xml:space="preserve">
<value>Select Type</value>
</data>
<data name="EnterConnectionParameters" xml:space="preserve">
<value>Enter Connection Parameters</value>
</data>
<data name="EnterConnectionString" xml:space="preserve">
<value>Enter Connection String</value>
</data>
<data name="ConnectionString.Text" xml:space="preserve">
<value>Settings: </value>
</data>
<data name="ConnectionString.HelpText" xml:space="preserve">
<value>A complete connection string including all parameters and delimiters</value>
</data>
<data name="Add" xml:space="preserve">
<value>Add</value>
</data>
<data name="Tenant.Text" xml:space="preserve">
<value>Database: </value>
</data>
<data name="Tenant.HelpText" xml:space="preserve">
<value>The database using this connection</value>
</data>
<data name="SqlQuery.Text" xml:space="preserve">
<value>SQL Query: </value>
</data>
<data name="SqlQuery.HelpText" xml:space="preserve">
<value>Enter a valid SQL query for the database</value>
</data>
<data name="Execute" xml:space="preserve">
<value>Execute</value>
</data>
<data name="Message.Required.Tenant" xml:space="preserve">
<value>You Must Select A Tenant And Provide A Valid SQL Query</value>
<value>You Must Select A Database Type And Provide A Valid SQL Query</value>
</data>
<data name="Message.Required.Connection" xml:space="preserve">
<value>You Must Provide A Connection Name And Settings</value>
</data>
<data name="Message.Connection.Added" xml:space="preserve">
<value>Connection Added Successfully</value>
</data>
<data name="Return.NoResult" xml:space="preserve">
<value>No Results Returned</value>
</data>
<data name="Tenant.HelpText" xml:space="preserve">
<value>Select the tenant associated with the database server</value>
</data>
<data name="SqlQuery.HelpText" xml:space="preserve">
<value>Enter the SQL query for the database server</value>
</data>
<data name="SqlQuery.Text" xml:space="preserve">
<value>SQL Query: </value>
</data>
<data name="Success.QueryExecuted" xml:space="preserve">
<value>SQL Query Executed</value>
</data>

View File

@ -285,4 +285,13 @@
<data name="Log.HelpText" xml:space="preserve">
<value>System log information for current day</value>
</data>
<data name="Clear" xml:space="preserve">
<value>Clear</value>
</data>
<data name="Success.ClearLog" xml:space="preserve">
<value>System Log Has Been Successfully Cleared</value>
</data>
<data name="Error.ClearLog" xml:space="preserve">
<value>Ann Error Occurred Clearing The System Log</value>
</data>
</root>

View File

@ -388,6 +388,12 @@
<value>Optionally provide the name of the role claim provided by the identity provider. These roles will be used in addition to any internal user roles assigned within the site.</value>
</data>
<data name="RoleClaimType.Text" xml:space="preserve">
<value>Role Claim Type:</value>
<value>Role Claim:</value>
</data>
<data name="ProfileClaimTypes.HelpText" xml:space="preserve">
<value>Optionally provide a comma delimited list of user profile claims provided by the identity provider, as well as mappings to your user profile definition. For example if the identity provider includes a 'given_name' claim and you have a 'FirstName' user profile definition you should specify 'given_name:FirstName'.</value>
</data>
<data name="ProfileClaimTypes.Text" xml:space="preserve">
<value>User Profile Claims:</value>
</data>
</root>

View File

@ -62,7 +62,7 @@ namespace Oqtane.Services
/// <summary>
/// Returns a key-value dictionary of all page module settings for the given page module
/// </summary>
/// <param name="pageId"></param>
/// <param name="pageModuleId"></param>
/// <returns></returns>
Task<Dictionary<string, string>> GetPageModuleSettingsAsync(int pageModuleId);
@ -107,7 +107,7 @@ namespace Oqtane.Services
/// <summary>
/// Returns a key-value dictionary of all user settings for the given user
/// </summary>
/// <param name="pageId"></param>
/// <param name="userId"></param>
/// <returns></returns>
Task<Dictionary<string, string>> GetUserSettingsAsync(int userId);
@ -122,7 +122,7 @@ namespace Oqtane.Services
/// <summary>
/// Returns a key-value dictionary of all folder settings for the given folder
/// </summary>
/// <param name="pageId"></param>
/// <param name="folderId"></param>
/// <returns></returns>
Task<Dictionary<string, string>> GetFolderSettingsAsync(int folderId);
@ -148,6 +148,21 @@ namespace Oqtane.Services
/// <returns></returns>
Task UpdateHostSettingsAsync(Dictionary<string, string> hostSettings);
/// <summary>
/// Returns a key-value dictionary of all settings for the given visitor
/// </summary>
/// <param name="visitorId"></param>
/// <returns></returns>
Task<Dictionary<string, string>> GetVisitorSettingsAsync(int visitorId);
/// <summary>
/// Updates a visitor setting
/// </summary>
/// <param name="visitorSettings"></param>
/// <param name="visitorId"></param>
/// <returns></returns>
Task UpdateVisitorSettingsAsync(Dictionary<string, string> visitorSettings, int visitorId);
/// <summary>
/// Returns a key-value dictionary of all settings for the given entityName
/// </summary>

View File

@ -32,11 +32,5 @@ namespace Oqtane.Services
/// <param name="settings"></param>
/// <returns></returns>
Task UpdateSystemInfoAsync(Dictionary<string, object> settings);
/// <summary>
/// updates a config value
/// </summary>
/// <returns></returns>
Task UpdateSystemInfoAsync(string settingKey, object settingValue);
}
}

View File

@ -111,15 +111,37 @@ namespace Oqtane.Services
await UpdateSettingsAsync(hostSettings, EntityNames.Host, -1);
}
public async Task<Dictionary<string, string>> GetVisitorSettingsAsync(int visitorId)
{
if (visitorId != -1)
{
return await GetSettingsAsync(EntityNames.Visitor, visitorId);
}
else
{
return new Dictionary<string, string>();
}
}
public async Task UpdateVisitorSettingsAsync(Dictionary<string, string> visitorSettings, int visitorId)
{
if (visitorId != -1)
{
await UpdateSettingsAsync(visitorSettings, EntityNames.Visitor, visitorId);
}
}
public async Task<Dictionary<string, string>> GetSettingsAsync(string entityName, int entityId)
{
var dictionary = new Dictionary<string, string>();
var settings = await GetJsonAsync<List<Setting>>($"{Apiurl}?entityname={entityName}&entityid={entityId}");
foreach(Setting setting in settings.OrderBy(item => item.SettingName).ToList())
if (settings != null)
{
foreach (Setting setting in settings.OrderBy(item => item.SettingName).ToList())
{
dictionary.Add(setting.SettingName, setting.SettingValue);
}
}
return dictionary;
}

View File

@ -3,6 +3,7 @@ using System.Threading.Tasks;
using System.Collections.Generic;
using Oqtane.Documentation;
using Oqtane.Shared;
using System.Net;
namespace Oqtane.Services
{
@ -32,9 +33,5 @@ namespace Oqtane.Services
{
await PostJsonAsync(Apiurl, settings);
}
public async Task UpdateSystemInfoAsync(string settingKey, object settingValue)
{
await PutJsonAsync($"{Apiurl}/{settingKey}/{settingValue}", "");
}
}
}

View File

@ -2,7 +2,7 @@
@inherits ModuleActionsBase
@attribute [OqtaneIgnore]
@if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions) && PageState.Action == Constants.DefaultAction)
@if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.PermissionList) && PageState.Action == Constants.DefaultAction)
{
<div class="app-moduleactions py-2 px-3">
<a class="nav-link dropdown-toggle" data-bs-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"></a>

View File

@ -30,11 +30,11 @@ namespace Oqtane.Themes.Controls
{
var actionList = new List<ActionViewModel>();
if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions))
if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.PermissionList))
{
actionList.Add(new ActionViewModel { Icon = Icons.Cog, Name = "Manage Settings", Action = async (u, m) => await Settings(u, m) });
if (UserSecurity.ContainsRole(ModuleState.Permissions, PermissionNames.View, RoleNames.Everyone))
if (UserSecurity.ContainsRole(ModuleState.PermissionList, PermissionNames.View, RoleNames.Everyone))
{
actionList.Add(new ActionViewModel { Icon = Icons.CircleX, Name = "Unpublish Module", Action = async (s, m) => await Unpublish(s, m) });
}
@ -44,7 +44,7 @@ namespace Oqtane.Themes.Controls
}
actionList.Add(new ActionViewModel { Icon = Icons.Trash, Name = "Delete Module", Action = async (u, m) => await DeleteModule(u, m) });
if (ModuleState.ModuleDefinition != null && ModuleState.ModuleDefinition.ServerManagerType != "")
if (ModuleState.ModuleDefinition != null && ModuleState.ModuleDefinition.IsPortable)
{
actionList.Add(new ActionViewModel { Name = "" });
actionList.Add(new ActionViewModel { Icon = Icons.CloudUpload, Name = "Import Content", Action = async (u, m) => await EditUrlAsync(u, m.ModuleId, "Import") });
@ -93,7 +93,7 @@ namespace Oqtane.Themes.Controls
protected async Task ModuleAction(ActionViewModel action)
{
if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, ModuleState.Permissions))
if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, ModuleState.PermissionList))
{
PageModule pagemodule = await PageModuleService.GetPageModuleAsync(ModuleState.PageModuleId);
@ -136,36 +136,32 @@ namespace Oqtane.Themes.Controls
private async Task<string> Publish(string url, PageModule pagemodule)
{
var permissions = UserSecurity.GetPermissionStrings(pagemodule.Module.Permissions);
foreach (var permissionstring in permissions)
var permissions = pagemodule.Module.PermissionList;
if (!permissions.Any(item => item.PermissionName == PermissionNames.View && item.RoleName == RoleNames.Everyone))
{
if (permissionstring.PermissionName == PermissionNames.View)
permissions.Add(new Permission(ModuleState.SiteId, EntityNames.Page, pagemodule.PageId, PermissionNames.View, RoleNames.Everyone, null, true));
}
if (!permissions.Any(item => item.PermissionName == PermissionNames.View && item.RoleName == RoleNames.Registered))
{
List<string> ids = permissionstring.Permissions.Split(';').ToList();
if (!ids.Contains(RoleNames.Everyone)) ids.Add(RoleNames.Everyone);
if (!ids.Contains(RoleNames.Registered)) ids.Add(RoleNames.Registered);
permissionstring.Permissions = string.Join(";", ids.ToArray());
permissions.Add(new Permission(ModuleState.SiteId, EntityNames.Page, pagemodule.PageId, PermissionNames.View, RoleNames.Registered, null, true));
}
}
pagemodule.Module.Permissions = UserSecurity.SetPermissionStrings(permissions);
pagemodule.Module.PermissionList = permissions;
await ModuleService.UpdateModuleAsync(pagemodule.Module);
return url;
}
private async Task<string> Unpublish(string url, PageModule pagemodule)
{
var permissions = UserSecurity.GetPermissionStrings(pagemodule.Module.Permissions);
foreach (var permissionstring in permissions)
var permissions = pagemodule.Module.PermissionList;
if (permissions.Any(item => item.PermissionName == PermissionNames.View && item.RoleName == RoleNames.Everyone))
{
if (permissionstring.PermissionName == PermissionNames.View)
permissions.Remove(permissions.First(item => item.PermissionName == PermissionNames.View && item.RoleName == RoleNames.Everyone));
}
if (permissions.Any(item => item.PermissionName == PermissionNames.View && item.RoleName == RoleNames.Registered))
{
List<string> ids = permissionstring.Permissions.Split(';').ToList();
ids.Remove(RoleNames.Everyone);
ids.Remove(RoleNames.Registered);
permissionstring.Permissions = string.Join(";", ids.ToArray());
permissions.Remove(permissions.First(item => item.PermissionName == PermissionNames.View && item.RoleName == RoleNames.Registered));
}
}
pagemodule.Module.Permissions = UserSecurity.SetPermissionStrings(permissions);
pagemodule.Module.PermissionList = permissions;
await ModuleService.UpdateModuleAsync(pagemodule.Module);
return url;
}

View File

@ -33,7 +33,7 @@
}
}
@if (_canViewAdminDashboard || UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions))
@if (_canViewAdminDashboard || UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.PermissionList))
{
<button type="button" class="btn @ButtonClass" data-bs-toggle="offcanvas" data-bs-target="#offcanvasControlPanel" aria-controls="offcanvasControlPanel">
<span class="oi oi-cog"></span>
@ -71,7 +71,7 @@
</div>
<div class="row d-flex">
<div class="col">
@if (UserSecurity.ContainsRole(PageState.Page.Permissions, PermissionNames.View, RoleNames.Everyone))
@if (UserSecurity.ContainsRole(PageState.Page.PermissionList, PermissionNames.View, RoleNames.Everyone))
{
<button type="button" class="btn btn-secondary col-12" @onclick=@(async () => Publish("unpublish"))>@Localizer["Page.Unpublish"]</button>
}
@ -107,7 +107,7 @@
}
}
@if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions))
@if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.PermissionList))
{
<div class="row">
<div class="col text-center">
@ -144,7 +144,7 @@
}
@foreach (var moduledefinition in _moduleDefinitions)
{
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Utilize, moduledefinition.Permissions))
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Utilize, moduledefinition.PermissionList))
{
if (moduledefinition.Runtimes == "" || moduledefinition.Runtimes.Contains(PageState.Runtime.ToString()))
{
@ -294,14 +294,14 @@
{
_canViewAdminDashboard = CanViewAdminDashboard();
_showEditMode = false;
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions))
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.PermissionList))
{
_showEditMode = true;
_pages?.Clear();
foreach (Page p in PageState.Pages)
{
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions))
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.PermissionList))
{
_pages.Add(p);
}
@ -319,7 +319,7 @@
{
foreach (var module in PageState.Modules.Where(item => item.PageId == PageState.Page.PageId))
{
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, module.Permissions))
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, module.PermissionList))
{
_showEditMode = true;
break;
@ -335,7 +335,7 @@
{
foreach (var page in PageState.Pages.Where(item => item.ParentId == admin?.PageId))
{
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, page.Permissions))
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, page.PermissionList))
{
return true;
}
@ -371,7 +371,7 @@
{
_modules = PageState.Modules
.Where(module => module.PageId == int.Parse(PageId) &&
UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, module.Permissions))
UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, module.PermissionList))
.ToList();
}
ModuleId = "-";
@ -380,7 +380,7 @@
private async Task AddModule()
{
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions))
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.PermissionList))
{
if ((ModuleType == "new" && ModuleDefinitionName != "-") || (ModuleType != "new" && ModuleId != "-"))
{
@ -392,20 +392,20 @@
module.ModuleDefinitionName = ModuleDefinitionName;
module.AllPages = false;
List<PermissionString> permissions = UserSecurity.GetPermissionStrings(PageState.Page.Permissions);
var permissions = new List<Permission>();
if (Visibility == "view")
{
// set module view permissions to page view permissions
permissions.Find(p => p.PermissionName == PermissionNames.View).Permissions = permissions.Find(p => p.PermissionName == PermissionNames.View).Permissions;
permissions = SetPermissions(permissions, module.SiteId, PermissionNames.View, PermissionNames.View);
}
else
{
// set module view permissions to page edit permissions
permissions.Find(p => p.PermissionName == PermissionNames.View).Permissions = permissions.Find(p => p.PermissionName == PermissionNames.Edit).Permissions;
permissions = SetPermissions(permissions, module.SiteId, PermissionNames.View, PermissionNames.Edit);
}
// set entityname
permissions.ForEach(item => item.EntityName = EntityNames.Module);
module.Permissions = UserSecurity.SetPermissionStrings(permissions);
// set module edit permissions to page edit permissions
permissions = SetPermissions(permissions, module.SiteId, PermissionNames.Edit, PermissionNames.Edit);
module.PermissionList = permissions;
module = await ModuleService.AddModuleAsync(module);
ModuleId = module.ModuleId.ToString();
@ -456,6 +456,15 @@
}
}
private List<Permission> SetPermissions(List<Permission> permissions, int siteId, string modulePermission, string pagePermission)
{
foreach (var permission in PageState.Page.PermissionList.Where(item => item.PermissionName == pagePermission))
{
permissions.Add(new Permission { SiteId = siteId, EntityName = EntityNames.Module, PermissionName = modulePermission, RoleId = permission.RoleId, UserId = permission.UserId, IsAuthorized = permission.IsAuthorized });
}
return permissions;
}
private async Task ToggleEditMode(bool EditMode)
{
if (_showEditMode)
@ -469,7 +478,7 @@
PageState.EditMode = true;
}
NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, "edit=" + ((PageState.EditMode) ? "1" : "0")));
NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, "edit=" + ((PageState.EditMode) ? "true" : "false")));
}
else
{
@ -477,7 +486,7 @@
{
await PageService.AddPageAsync(PageState.Page.PageId, PageState.User.UserId);
PageState.EditMode = true;
NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, "edit=" + ((PageState.EditMode) ? "1" : "0")));
NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, "edit=" + ((PageState.EditMode) ? "true" : "false")));
}
}
}
@ -525,34 +534,19 @@
private async void Publish(string action)
{
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions))
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.PermissionList))
{
List<PermissionString> permissions;
// publish/unpublish page
var page = PageState.Page;
permissions = UserSecurity.GetPermissionStrings(page.Permissions);
foreach (var permissionstring in permissions)
var permissions = PageState.Page.PermissionList;
if (!permissions.Any(item => item.PermissionName == PermissionNames.View && item.RoleName == RoleNames.Everyone))
{
if (permissionstring.PermissionName == PermissionNames.View)
{
List<string> ids = permissionstring.Permissions.Split(';').ToList();
switch (action)
{
case "publish":
if (!ids.Contains(RoleNames.Everyone)) ids.Add(RoleNames.Everyone);
if (!ids.Contains(RoleNames.Registered)) ids.Add(RoleNames.Registered);
break;
case "unpublish":
ids.Remove(RoleNames.Everyone);
ids.Remove(RoleNames.Registered);
break;
permissions.Add(new Permission(PageState.Site.SiteId, EntityNames.Page, PageState.Page.PageId, PermissionNames.View, RoleNames.Everyone, null, true));
}
permissionstring.Permissions = string.Join(";", ids.ToArray());
if (!permissions.Any(item => item.PermissionName == PermissionNames.View && item.RoleName == RoleNames.Registered))
{
permissions.Add(new Permission(PageState.Site.SiteId, EntityNames.Page, PageState.Page.PageId, PermissionNames.View, RoleNames.Registered, null, true));
}
}
page.Permissions = UserSecurity.SetPermissionStrings(permissions);
await PageService.UpdatePageAsync(page);
PageState.Page.PermissionList = permissions;
await PageService.UpdatePageAsync(PageState.Page);
NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, true));
}
}

View File

@ -1,8 +1,10 @@
using System;
using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using Oqtane.Enums;
using Oqtane.Models;
using Oqtane.Providers;
using Oqtane.Security;
using Oqtane.Services;
@ -22,21 +24,19 @@ namespace Oqtane.Themes.Controls
protected void LoginUser()
{
var returnurl = PageState.Alias.Path;
if (PageState.Page.Path != "/")
{
returnurl += "/" + PageState.Page.Path;
}
NavigationManager.NavigateTo(NavigateUrl("login", "?returnurl=" + returnurl));
Route route = new Route(PageState.Uri.AbsoluteUri, PageState.Alias.Path);
NavigationManager.NavigateTo(NavigateUrl("login", "?returnurl=" + WebUtility.UrlEncode(route.PathAndQuery)));
}
protected async Task LogoutUser()
{
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);
// check if anonymous user can access page
var url = PageState.Alias.Path + "/" + PageState.Page.Path;
if (!UserSecurity.IsAuthorized(null, PermissionNames.View, PageState.Page.Permissions))
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;
}

View File

@ -32,7 +32,7 @@ namespace Oqtane.Themes.Controls
var securityLevel = int.MaxValue;
foreach (Page p in PageState.Pages.Where(item => item.IsNavigation))
{
if (p.Level <= securityLevel && UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions))
if (p.Level <= securityLevel && UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.PermissionList))
{
securityLevel = int.MaxValue;
yield return p;

View File

@ -43,7 +43,7 @@
container = (!string.IsNullOrEmpty(PageState.Site.AdminContainerType)) ? PageState.Site.AdminContainerType : Constants.DefaultAdminContainer;
}
if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions) && PageState.Action == Constants.DefaultAction)
if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.PermissionList) && PageState.Action == Constants.DefaultAction)
{
_useadminborder = true;
}

View File

@ -30,7 +30,7 @@ else
protected override void OnParametersSet()
{
if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions) && PageState.Action == Constants.DefaultAction)
if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.PermissionList) && PageState.Action == Constants.DefaultAction)
{
_useadminborder = true;
_panetitle = "<div class=\"app-pane-admin-title\">" + Name + " Pane</div>";
@ -67,7 +67,7 @@ else
bool authorized = false;
if (Constants.DefaultModuleActions.Contains(PageState.Action))
{
authorized = UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions);
authorized = UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.PermissionList);
}
else
{
@ -77,10 +77,10 @@ else
authorized = true;
break;
case SecurityAccessLevel.View:
authorized = UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, module.Permissions);
authorized = UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, module.PermissionList);
break;
case SecurityAccessLevel.Edit:
authorized = UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, module.Permissions);
authorized = UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, module.PermissionList);
break;
case SecurityAccessLevel.Admin:
authorized = UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin);
@ -111,7 +111,7 @@ else
if (module != null && module.Pane.ToLower() == Name.ToLower())
{
// check if user is authorized to view module
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, module.Permissions))
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, module.PermissionList))
{
CreateComponent(builder, module);
}
@ -122,7 +122,7 @@ else
foreach (Module module in PageState.Modules.Where(item => item.PageId == PageState.Page.PageId && item.Pane.ToLower() == Name.ToLower()).OrderBy(x => x.Order).ToArray())
{
// check if user is authorized to view module
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, module.Permissions))
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, module.PermissionList))
{
CreateComponent(builder, module);
}

View File

@ -124,6 +124,10 @@
editmode = PageState.EditMode;
lastsyncdate = PageState.LastSyncDate;
}
if (PageState?.Page.Path != route.PagePath)
{
editmode = false; // reset edit mode when navigating to different page
}
// get user
if (PageState == null || refresh || PageState.Alias.SiteId != SiteState.Alias.SiteId)
@ -181,7 +185,6 @@
if (PageState == null || refresh || PageState.Page.Path != route.PagePath)
{
page = site.Pages.FirstOrDefault(item => item.Path.Equals(route.PagePath, StringComparison.OrdinalIgnoreCase));
editmode = false;
}
else
{
@ -204,7 +207,7 @@
if (page != null)
{
// check if user is authorized to view page
if (UserSecurity.IsAuthorized(user, PermissionNames.View, page.Permissions))
if (UserSecurity.IsAuthorized(user, PermissionNames.View, page.PermissionList))
{
// load additional metadata for current page
page = await ProcessPage(page, site, user);
@ -250,7 +253,7 @@
if (user == null)
{
// redirect to login page if user not logged in as they may need to be authenticated
NavigationManager.NavigateTo(Utilities.NavigateUrl(SiteState.Alias.Path, "login", "?returnurl=" + route.AbsolutePath));
NavigationManager.NavigateTo(Utilities.NavigateUrl(SiteState.Alias.Path, "login", "?returnurl=" + WebUtility.UrlEncode(route.PathAndQuery)));
}
else
{
@ -299,22 +302,28 @@
{
query = query.Substring(1); // ignore "?"
}
foreach (string kvp in query.Split(new[] { '&' }, StringSplitOptions.RemoveEmptyEntries))
foreach (string kvp in query.Split('&', StringSplitOptions.RemoveEmptyEntries))
{
if (kvp != "")
{
if (kvp.Contains("="))
{
string[] pair = kvp.Split('=');
if (!querystring.ContainsKey(pair[0]))
{
querystring.Add(pair[0], pair[1]);
}
}
else
{
if (!querystring.ContainsKey(kvp))
{
querystring.Add(kvp, "true"); // default parameter when no value is provided
}
}
}
}
}
return querystring;
}
@ -358,7 +367,7 @@
}
if (!string.IsNullOrEmpty(panes))
{
page.Panes = panes.Replace(";", ",").Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList();
page.Panes = panes.Replace(";", ",").Split(',', StringSplitOptions.RemoveEmptyEntries).ToList();
if (!page.Panes.Contains(PaneNames.Default) && !page.Panes.Contains(PaneNames.Admin))
{
_error = "The Current Theme Does Not Contain A Default Or Admin Pane";
@ -407,7 +416,7 @@
// check if the module defines custom action routes
if (module.ModuleDefinition.ControlTypeRoutes != "")
{
foreach (string route in module.ModuleDefinition.ControlTypeRoutes.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries))
foreach (string route in module.ModuleDefinition.ControlTypeRoutes.Split(';', StringSplitOptions.RemoveEmptyEntries))
{
if (route.StartsWith(action + "="))
{

View File

@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Version>3.3.1</Version>
<Version>3.4.2</Version>
<Product>Oqtane</Product>
<Authors>Shaun Walker</Authors>
<Company>.NET Foundation</Company>
@ -10,7 +10,7 @@
<Copyright>.NET Foundation</Copyright>
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.3.1</PackageReleaseNotes>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.4.2</PackageReleaseNotes>
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>

View File

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

View File

@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Version>3.3.1</Version>
<Version>3.4.2</Version>
<Product>Oqtane</Product>
<Authors>Shaun Walker</Authors>
<Company>.NET Foundation</Company>
@ -10,7 +10,7 @@
<Copyright>.NET Foundation</Copyright>
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.3.1</PackageReleaseNotes>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.4.2</PackageReleaseNotes>
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>

View File

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

View File

@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Version>3.3.1</Version>
<Version>3.4.2</Version>
<Product>Oqtane</Product>
<Authors>Shaun Walker</Authors>
<Company>.NET Foundation</Company>
@ -10,7 +10,7 @@
<Copyright>.NET Foundation</Copyright>
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.3.1</PackageReleaseNotes>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.4.2</PackageReleaseNotes>
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>

View File

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

View File

@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Version>3.3.1</Version>
<Version>3.4.2</Version>
<Product>Oqtane</Product>
<Authors>Shaun Walker</Authors>
<Company>.NET Foundation</Company>
@ -10,7 +10,7 @@
<Copyright>.NET Foundation</Copyright>
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.3.1</PackageReleaseNotes>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.4.2</PackageReleaseNotes>
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>

View File

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

View File

@ -212,6 +212,8 @@ public static class MauiProgram
private static void RegisterModuleServices(Assembly assembly, IServiceCollection services)
{
// dynamically register module scoped services
try
{
var implementationTypes = assembly.GetInterfaces<IService>();
foreach (var implementationType in implementationTypes)
{
@ -222,8 +224,15 @@ public static class MauiProgram
}
}
}
catch
{
// could not interrogate assembly - likely missing dependencies
}
}
private static void RegisterClientStartups(Assembly assembly, IServiceCollection services)
{
try
{
var startUps = assembly.GetInstances<IClientStartup>();
foreach (var startup in startUps)
@ -231,4 +240,9 @@ public static class MauiProgram
startup.ConfigureServices(services);
}
}
catch
{
// could not interrogate assembly - likely missing dependencies
}
}
}

View File

@ -1,12 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">
<PropertyGroup>
<TargetFrameworks>net6.0-android;net6.0-ios;net6.0-maccatalyst</TargetFrameworks>
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net6.0-windows10.0.19041.0</TargetFrameworks>
<!-- Uncomment to also build the tizen app. You will need to install tizen by following this: https://github.com/Samsung/Tizen.NET -->
<!-- <TargetFrameworks>net6.0-android;net6.0-ios;net6.0-maccatalyst</TargetFrameworks> -->
<!-- <TargetFrameworks>$(TargetFrameworks);net6.0-tizen</TargetFrameworks> -->
<OutputType>Exe</OutputType>
<Version>3.3.1</Version>
<Version>3.4.2</Version>
<Product>Oqtane</Product>
<Authors>Shaun Walker</Authors>
<Company>.NET Foundation</Company>
@ -14,7 +14,7 @@
<Copyright>.NET Foundation</Copyright>
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.3.1</PackageReleaseNotes>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v3.4.2</PackageReleaseNotes>
<RepositoryUrl>https://github.com/oqtane/oqtane.framework</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
<RootNamespace>Oqtane.Maui</RootNamespace>
@ -31,7 +31,7 @@
<ApplicationIdGuid>0E29FC31-1B83-48ED-B6E0-9F3C67B775D4</ApplicationIdGuid>
<!-- Versions -->
<ApplicationDisplayVersion>3.3.1</ApplicationDisplayVersion>
<ApplicationDisplayVersion>3.4.2</ApplicationDisplayVersion>
<ApplicationVersion>1</ApplicationVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">14.2</SupportedOSPlatformVersion>
@ -71,8 +71,8 @@
<PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Localization" Version="6.0.3" />
<PackageReference Include="System.Net.Http.Json" Version="6.0.0" />
<PackageReference Include="Oqtane.Client" Version="3.3.1" />
<PackageReference Include="Oqtane.Shared" Version="3.3.1" />
<PackageReference Include="Oqtane.Client" Version="3.4.2" />
<PackageReference Include="Oqtane.Shared" Version="3.4.2" />
</ItemGroup>
</Project>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1 +1 @@
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net6.0\publish\*" -DestinationPath "Oqtane.Framework.3.3.1.Install.zip" -Force
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net6.0\publish\*" -DestinationPath "Oqtane.Framework.3.4.2.Install.zip" -Force

View File

@ -1 +1 @@
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net6.0\publish\*" -DestinationPath "Oqtane.Framework.3.3.1.Upgrade.zip" -Force
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net6.0\publish\*" -DestinationPath "Oqtane.Framework.3.4.2.Upgrade.zip" -Force

View File

@ -20,7 +20,6 @@ using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Formats.Png;
using System.Net.Http;
using Oqtane.Migrations.Tenant;
// ReSharper disable StringIndexOfIsCultureSpecific.1
@ -57,7 +56,7 @@ namespace Oqtane.Controllers
if (int.TryParse(folder, out folderid))
{
Folder Folder = _folders.GetFolder(folderid);
if (Folder != null && Folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.Browse, Folder.Permissions))
if (Folder != null && Folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.Browse, Folder.PermissionList))
{
files = _files.GetFiles(folderid).ToList();
}
@ -99,7 +98,7 @@ namespace Oqtane.Controllers
List<Models.File> files;
Folder folder = _folders.GetFolder(siteId, WebUtility.UrlDecode(path));
if (folder != null && folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.Browse, folder.Permissions))
if (folder != null && folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.Browse, folder.PermissionList))
{
files = _files.GetFiles(folder.FolderId).ToList();
}
@ -118,7 +117,7 @@ namespace Oqtane.Controllers
public Models.File Get(int id)
{
Models.File file = _files.GetFile(id);
if (file != null && file.Folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.View, file.Folder.Permissions))
if (file != null && file.Folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.View, file.Folder.PermissionList))
{
return file;
}
@ -142,7 +141,7 @@ namespace Oqtane.Controllers
{
if (File.Name != file.Name || File.FolderId != file.FolderId)
{
file.Folder = _folders.GetFolder(file.FolderId);
file.Folder = _folders.GetFolder(file.FolderId, false);
string folderpath = _folders.GetFolderPath(file.Folder);
if (!Directory.Exists(folderpath))
{
@ -151,7 +150,7 @@ namespace Oqtane.Controllers
System.IO.File.Move(_files.GetFilePath(File), Path.Combine(folderpath, file.Name));
}
var newfile = CreateFile(file.Name, file.Folder.FolderId, _files.GetFilePath(file));
var newfile = CreateFile(File.Name, file.Folder.FolderId, _files.GetFilePath(file));
if (newfile != null)
{
file.Extension = newfile.Extension;
@ -216,7 +215,7 @@ namespace Oqtane.Controllers
folder = _folders.GetFolder(FolderId);
}
if (folder != null && folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.Edit, folder.Permissions))
if (folder != null && folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.Edit, folder.PermissionList))
{
string folderPath = _folders.GetFolderPath(folder);
CreateDirectory(folderPath);
@ -311,7 +310,7 @@ namespace Oqtane.Controllers
if (int.TryParse(folder, out FolderId))
{
Folder Folder = _folders.GetFolder(FolderId);
if (Folder != null && Folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.Edit, Folder.Permissions))
if (Folder != null && Folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.Edit, Folder.PermissionList))
{
folderPath = _folders.GetFolderPath(Folder);
}
@ -498,7 +497,7 @@ namespace Oqtane.Controllers
private IActionResult Download(int id, bool asAttachment)
{
var file = _files.GetFile(id);
if (file != null && file.Folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.View, file.Folder.Permissions))
if (file != null && file.Folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.View, file.Folder.PermissionList))
{
var filepath = _files.GetFilePath(file);
if (System.IO.File.Exists(filepath))
@ -533,7 +532,7 @@ namespace Oqtane.Controllers
public IActionResult GetImage(int id, int width, int height, string mode, string position, string background, string rotate, string recreate)
{
var file = _files.GetFile(id);
if (file != null && file.Folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.View, file.Folder.Permissions))
if (file != null && file.Folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.View, file.Folder.PermissionList))
{
if (Constants.ImageFiles.Split(',').Contains(file.Extension.ToLower()))
{
@ -551,7 +550,7 @@ namespace Oqtane.Controllers
string imagepath = filepath.Replace(Path.GetExtension(filepath), "." + width.ToString() + "x" + height.ToString() + ".png");
if (!System.IO.File.Exists(imagepath) || bool.Parse(recreate))
{
if ((_userPermissions.IsAuthorized(User, PermissionNames.Edit, file.Folder.Permissions) ||
if ((_userPermissions.IsAuthorized(User, PermissionNames.Edit, file.Folder.PermissionList) ||
!string.IsNullOrEmpty(file.Folder.ImageSizes) && file.Folder.ImageSizes.ToLower().Split(",").Contains(width.ToString() + "x" + height.ToString())))
{
imagepath = CreateImage(filepath, width, height, mode, position, background, rotate, imagepath);
@ -659,10 +658,10 @@ namespace Oqtane.Controllers
var file = _files.GetFile(folderid, filename);
int size = 0;
var folder = _folders.GetFolder(folderid);
var folder = _folders.GetFolder(folderid, false);
if (folder.Capacity != 0)
{
foreach (var f in _files.GetFiles(folderid))
foreach (var f in _files.GetFiles(folderid, false))
{
size += f.Size;
}

View File

@ -43,7 +43,7 @@ namespace Oqtane.Controllers
{
foreach (Folder folder in _folders.GetFolders(SiteId))
{
if (_userPermissions.IsAuthorized(User, PermissionNames.Browse, folder.Permissions))
if (_userPermissions.IsAuthorized(User, PermissionNames.Browse, folder.PermissionList))
{
folders.Add(folder);
}
@ -64,7 +64,7 @@ namespace Oqtane.Controllers
public Folder Get(int id)
{
Folder folder = _folders.GetFolder(id);
if (folder != null && folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.Browse, folder.Permissions))
if (folder != null && folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.Browse, folder.PermissionList))
{
return folder;
}
@ -85,7 +85,7 @@ namespace Oqtane.Controllers
folderPath += "/";
}
Folder folder = _folders.GetFolder(siteId, folderPath);
if (folder != null && folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.Browse, folder.Permissions))
if (folder != null && folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.Browse, folder.PermissionList))
{
return folder;
}
@ -104,16 +104,16 @@ namespace Oqtane.Controllers
{
if (ModelState.IsValid && folder.SiteId == _alias.SiteId)
{
string permissions;
List<Permission> permissions;
if (folder.ParentId != null)
{
permissions = _folders.GetFolder(folder.ParentId.Value).Permissions;
permissions = _folders.GetFolder(folder.ParentId.Value).PermissionList;
}
else
{
permissions = new List<Permission> {
new Permission(PermissionNames.Edit, RoleNames.Admin, true),
}.EncodePermissions();
};
}
if (_userPermissions.IsAuthorized(User, PermissionNames.Edit, permissions))
{

View File

@ -52,7 +52,7 @@ namespace Oqtane.Controllers
{
var installation = new Installation { Success = false, Message = "" };
if (ModelState.IsValid && (User.IsInRole(RoleNames.Host) || string.IsNullOrEmpty(_configManager.GetSetting("ConnectionStrings:" + SettingKeys.ConnectionStringKey, ""))))
if (ModelState.IsValid && (User.IsInRole(RoleNames.Host) || string.IsNullOrEmpty(_configManager.GetSetting($"{SettingKeys.ConnectionStringsSection}:{SettingKeys.ConnectionStringKey}", ""))))
{
installation = _databaseManager.Install(config);

View File

@ -47,17 +47,18 @@ namespace Oqtane.Controllers
int SiteId;
if (int.TryParse(siteid, out SiteId) && SiteId == _alias.SiteId)
{
List<ModuleDefinition> moduledefinitions = _moduleDefinitions.GetModuleDefinitions(SiteId).ToList();
List<Setting> settings = _settings.GetSettings(EntityNames.Module).ToList();
foreach (PageModule pagemodule in _pageModules.GetPageModules(SiteId))
{
if (_userPermissions.IsAuthorized(User, PermissionNames.View, pagemodule.Module.Permissions))
if (_userPermissions.IsAuthorized(User, PermissionNames.View, pagemodule.Module.PermissionList))
{
Module module = new Module();
module.SiteId = pagemodule.Module.SiteId;
module.ModuleDefinitionName = pagemodule.Module.ModuleDefinitionName;
module.AllPages = pagemodule.Module.AllPages;
module.Permissions = pagemodule.Module.Permissions;
module.PermissionList = pagemodule.Module.PermissionList;
module.CreatedBy = pagemodule.Module.CreatedBy;
module.CreatedOn = pagemodule.Module.CreatedOn;
module.ModifiedBy = pagemodule.Module.ModifiedBy;
@ -74,8 +75,10 @@ namespace Oqtane.Controllers
module.Order = pagemodule.Order;
module.ContainerType = pagemodule.ContainerType;
module.ModuleDefinition = _moduleDefinitions.FilterModuleDefinition(moduledefinitions.Find(item => item.ModuleDefinitionName == module.ModuleDefinitionName));
module.Settings = settings.Where(item => item.EntityId == pagemodule.ModuleId)
.Where(item => !item.IsPrivate || _userPermissions.IsAuthorized(User, PermissionNames.Edit, pagemodule.Module.Permissions))
.Where(item => !item.IsPrivate || _userPermissions.IsAuthorized(User, PermissionNames.Edit, pagemodule.Module.PermissionList))
.ToDictionary(setting => setting.SettingName, setting => setting.SettingValue);
modules.Add(module);
@ -97,12 +100,12 @@ namespace Oqtane.Controllers
public Module Get(int id)
{
Module module = _modules.GetModule(id);
if (module != null && module.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User,PermissionNames.View, module.Permissions))
if (module != null && module.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User,PermissionNames.View, module.PermissionList))
{
List<ModuleDefinition> moduledefinitions = _moduleDefinitions.GetModuleDefinitions(module.SiteId).ToList();
module.ModuleDefinition = moduledefinitions.Find(item => item.ModuleDefinitionName == module.ModuleDefinitionName);
module.ModuleDefinition = _moduleDefinitions.FilterModuleDefinition(moduledefinitions.Find(item => item.ModuleDefinitionName == module.ModuleDefinitionName));
module.Settings = _settings.GetSettings(EntityNames.Module, id)
.Where(item => !item.IsPrivate || _userPermissions.IsAuthorized(User, PermissionNames.Edit, module.Permissions))
.Where(item => !item.IsPrivate || _userPermissions.IsAuthorized(User, PermissionNames.Edit, module.PermissionList))
.ToDictionary(setting => setting.SettingName, setting => setting.SettingValue);
return module;
}

View File

@ -15,6 +15,7 @@ using System;
using Microsoft.Extensions.DependencyInjection;
using System.Text.Json;
using System.Net;
using Oqtane.Modules;
namespace Oqtane.Controllers
{
@ -22,6 +23,9 @@ namespace Oqtane.Controllers
public class ModuleDefinitionController : Controller
{
private readonly IModuleDefinitionRepository _moduleDefinitions;
private readonly IModuleRepository _modules;
private readonly IPageModuleRepository _pagemodules;
private readonly IPermissionRepository _permissions;
private readonly ITenantRepository _tenants;
private readonly ISqlRepository _sql;
private readonly IUserPermissions _userPermissions;
@ -33,9 +37,12 @@ namespace Oqtane.Controllers
private readonly ILogManager _logger;
private readonly Alias _alias;
public ModuleDefinitionController(IModuleDefinitionRepository moduleDefinitions, ITenantRepository tenants, ISqlRepository sql, IUserPermissions userPermissions, IInstallationManager installationManager, IWebHostEnvironment environment, IServiceProvider serviceProvider, ITenantManager tenantManager, ISyncManager syncManager, ILogManager logger)
public ModuleDefinitionController(IModuleDefinitionRepository moduleDefinitions, IModuleRepository module, IPageModuleRepository pageModule, IPermissionRepository permission, ITenantRepository tenants, ISqlRepository sql, IUserPermissions userPermissions, IInstallationManager installationManager, IWebHostEnvironment environment, IServiceProvider serviceProvider, ITenantManager tenantManager, ISyncManager syncManager, ILogManager logger)
{
_moduleDefinitions = moduleDefinitions;
_modules = module;
_pagemodules = pageModule;
_permissions = permission;
_tenants = tenants;
_sql = sql;
_userPermissions = userPermissions;
@ -58,7 +65,7 @@ namespace Oqtane.Controllers
List<ModuleDefinition> moduledefinitions = new List<ModuleDefinition>();
foreach (ModuleDefinition moduledefinition in _moduleDefinitions.GetModuleDefinitions(SiteId))
{
if (_userPermissions.IsAuthorized(User, PermissionNames.Utilize, moduledefinition.Permissions))
if (_userPermissions.IsAuthorized(User, PermissionNames.Utilize, moduledefinition.PermissionList))
{
if (string.IsNullOrEmpty(moduledefinition.Version)) moduledefinition.Version = new Version(1, 0, 0).ToString();
moduledefinitions.Add(moduledefinition);
@ -82,7 +89,7 @@ namespace Oqtane.Controllers
if (int.TryParse(siteid, out SiteId) && SiteId == _alias.SiteId)
{
ModuleDefinition moduledefinition = _moduleDefinitions.GetModuleDefinition(id, SiteId);
if (_userPermissions.IsAuthorized(User, PermissionNames.Utilize, moduledefinition.Permissions))
if (_userPermissions.IsAuthorized(User, PermissionNames.Utilize, moduledefinition.PermissionList))
{
if (string.IsNullOrEmpty(moduledefinition.Version)) moduledefinition.Version = new Version(1, 0, 0).ToString();
return moduledefinition;
@ -228,6 +235,27 @@ namespace Oqtane.Controllers
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Static Resources Folder Removed For {ModuleDefinitionName}", moduledefinition.ModuleDefinitionName);
}
// remove PageModule and Module
List<Models.Module> modulesToRemove = _modules.GetModules(moduledefinition.SiteId).Where(m => m.ModuleDefinitionName == moduledefinition.ModuleDefinitionName).ToList();
foreach (Models.Module moduleToRemove in modulesToRemove)
{
// Get the PageModule items associated with the Module item to be removed
List<PageModule> pageModulesToRemove = _pagemodules.GetPageModules(moduledefinition.SiteId).Where(pm => pm.ModuleId == moduleToRemove.ModuleId).ToList();
foreach(PageModule pageModule in pageModulesToRemove)
{
// Remove the PageModule item
_pagemodules.DeletePageModule(pageModule.PageModuleId);
}
// Remove Permissions
_permissions.DeletePermissions(moduledefinition.SiteId, EntityNames.Module, moduleToRemove.ModuleId);
// Remove the Module item
_modules.DeleteModule(moduleToRemove.ModuleId);
}
// remove module definition
_moduleDefinitions.DeleteModuleDefinition(id);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.ModuleDefinition, moduledefinition.ModuleDefinitionId, SyncEventActions.Delete);

View File

@ -54,10 +54,10 @@ namespace Oqtane.Controllers
foreach (Page page in _pages.GetPages(SiteId))
{
if (_userPermissions.IsAuthorized(User, PermissionNames.View, page.Permissions))
if (_userPermissions.IsAuthorized(User, PermissionNames.View, page.PermissionList))
{
page.Settings = settings.Where(item => item.EntityId == page.PageId)
.Where(item => !item.IsPrivate || _userPermissions.IsAuthorized(User, PermissionNames.Edit, page.Permissions))
.Where(item => !item.IsPrivate || _userPermissions.IsAuthorized(User, PermissionNames.Edit, page.PermissionList))
.ToDictionary(setting => setting.SettingName, setting => setting.SettingValue);
pages.Add(page);
}
@ -86,10 +86,10 @@ namespace Oqtane.Controllers
{
page = _pages.GetPage(id, int.Parse(userid));
}
if (page != null && page.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User,PermissionNames.View, page.Permissions))
if (page != null && page.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.View, page.PermissionList))
{
page.Settings = _settings.GetSettings(EntityNames.Page, page.PageId)
.Where(item => !item.IsPrivate || _userPermissions.IsAuthorized(User, PermissionNames.Edit, page.Permissions))
.Where(item => !item.IsPrivate || _userPermissions.IsAuthorized(User, PermissionNames.Edit, page.PermissionList))
.ToDictionary(setting => setting.SettingName, setting => setting.SettingValue);
return page;
}
@ -106,10 +106,10 @@ namespace Oqtane.Controllers
public Page Get(string path, int siteid)
{
Page page = _pages.GetPage(WebUtility.UrlDecode(path), siteid);
if (page != null && page.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.View, page.Permissions))
if (page != null && page.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.View, page.PermissionList))
{
page.Settings = _settings.GetSettings(EntityNames.Page, page.PageId)
.Where(item => !item.IsPrivate || _userPermissions.IsAuthorized(User, PermissionNames.Edit, page.Permissions))
.Where(item => !item.IsPrivate || _userPermissions.IsAuthorized(User, PermissionNames.Edit, page.PermissionList))
.ToDictionary(setting => setting.SettingName, setting => setting.SettingValue);
return page;
}
@ -128,19 +128,19 @@ namespace Oqtane.Controllers
{
if (ModelState.IsValid && page.SiteId == _alias.SiteId)
{
string permissions;
List<Permission> permissions;
if (page.ParentId != null)
{
permissions = _pages.GetPage(page.ParentId.Value).Permissions;
permissions = _pages.GetPage(page.ParentId.Value).PermissionList;
}
else
{
permissions = new List<Permission> {
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
}.EncodePermissions();
};
}
if (_userPermissions.IsAuthorized(User,PermissionNames.Edit, permissions))
if (_userPermissions.IsAuthorized(User, PermissionNames.Edit, permissions))
{
page = _pages.AddPage(page);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Page, page.PageId, SyncEventActions.Create);
@ -194,10 +194,10 @@ namespace Oqtane.Controllers
page.ThemeType = parent.ThemeType;
page.DefaultContainerType = parent.DefaultContainerType;
page.Icon = parent.Icon;
page.Permissions = new List<Permission> {
page.PermissionList = new List<Permission> {
new Permission(PermissionNames.View, int.Parse(userid), true),
new Permission(PermissionNames.Edit, int.Parse(userid), true)
}.EncodePermissions();
};
page.IsPersonalizable = false;
page.UserId = int.Parse(userid);
page = _pages.AddPage(page);
@ -213,10 +213,10 @@ namespace Oqtane.Controllers
module.PageId = page.PageId;
module.ModuleDefinitionName = pm.Module.ModuleDefinitionName;
module.AllPages = false;
module.Permissions = new List<Permission> {
module.PermissionList = new List<Permission> {
new Permission(PermissionNames.View, int.Parse(userid), true),
new Permission(PermissionNames.Edit, int.Parse(userid), true)
}.EncodePermissions();
};
module = _modules.AddModule(module);
string content = _modules.ExportModule(pm.ModuleId);
@ -274,9 +274,8 @@ namespace Oqtane.Controllers
}
// get differences between current and new page permissions
var newPermissions = _permissionRepository.DecodePermissions(page.Permissions, page.SiteId, EntityNames.Page, page.PageId).ToList();
var added = GetPermissionsDifferences(newPermissions, currentPermissions);
var removed = GetPermissionsDifferences(currentPermissions, newPermissions);
var added = GetPermissionsDifferences(page.PermissionList, currentPermissions);
var removed = GetPermissionsDifferences(currentPermissions, page.PermissionList);
// synchronize module permissions
if (added.Count > 0 || removed.Count > 0)
@ -285,7 +284,7 @@ namespace Oqtane.Controllers
{
var modulePermissions = _permissionRepository.GetPermissions(pageModule.Module.SiteId, EntityNames.Module, pageModule.Module.ModuleId).ToList();
// permissions added
foreach(Permission permission in added)
foreach (Permission permission in added)
{
if (!modulePermissions.Any(item => item.PermissionName == permission.PermissionName
&& item.RoleId == permission.RoleId && item.UserId == permission.UserId && item.IsAuthorized == permission.IsAuthorized))
@ -346,7 +345,7 @@ namespace Oqtane.Controllers
[Authorize(Roles = RoleNames.Registered)]
public void Put(int siteid, int pageid, int? parentid)
{
if (siteid == _alias.SiteId && siteid == _alias.SiteId && _pages.GetPage(pageid, false) != null && _userPermissions.IsAuthorized(User, siteid, EntityNames.Page, pageid, PermissionNames.Edit))
if (siteid == _alias.SiteId && _pages.GetPage(pageid, false) != null && _userPermissions.IsAuthorized(User, siteid, EntityNames.Page, pageid, PermissionNames.Edit))
{
int order = 1;
List<Page> pages = _pages.GetPages(siteid).ToList();

View File

@ -38,7 +38,7 @@ namespace Oqtane.Controllers
public PageModule Get(int id)
{
PageModule pagemodule = _pageModules.GetPageModule(id);
if (pagemodule != null && pagemodule.Module.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.View, pagemodule.Module.Permissions))
if (pagemodule != null && pagemodule.Module.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.View, pagemodule.Module.PermissionList))
{
return pagemodule;
}
@ -55,7 +55,7 @@ namespace Oqtane.Controllers
public PageModule Get(int pageid, int moduleid)
{
PageModule pagemodule = _pageModules.GetPageModule(pageid, moduleid);
if (pagemodule != null && pagemodule.Module.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.View, pagemodule.Module.Permissions))
if (pagemodule != null && pagemodule.Module.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.View, pagemodule.Module.PermissionList))
{
return pagemodule;
}

View File

@ -46,7 +46,7 @@ namespace Oqtane.Controllers
_identityCache = identityCache;
_logger = logger;
_alias = tenantManager.GetAlias();
_visitorCookie = "APP_VISITOR_" + _alias.SiteId.ToString();
_visitorCookie = Constants.VisitorCookiePrefix + _alias.SiteId.ToString();
}
// GET: api/<controller>
@ -63,10 +63,14 @@ namespace Oqtane.Controllers
}
}
else
{
// suppress unauthorized visitor logging as it is usually caused by clients that do not support cookies
if (entityName != EntityNames.Visitor)
{
_logger.Log(LogLevel.Error, this, LogFunction.Read, "User Not Authorized To Access Settings {EntityName} {EntityId}", entityName, entityId);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
}
}
return settings;
}
@ -84,9 +88,12 @@ namespace Oqtane.Controllers
return setting;
}
else
{
if (entityName != EntityNames.Visitor)
{
_logger.Log(LogLevel.Error, this, LogFunction.Read, "User Not Authorized To Access Setting {EntityName} {SettingId}", entityName, id);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
}
return null;
}
}
@ -102,9 +109,12 @@ namespace Oqtane.Controllers
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Setting Added {Setting}", setting);
}
else
{
if (setting.EntityName != EntityNames.Visitor)
{
_logger.Log(LogLevel.Error, this, LogFunction.Create, "User Not Authorized To Add Setting {Setting}", setting);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
}
setting = null;
}
return setting;
@ -121,9 +131,12 @@ namespace Oqtane.Controllers
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Setting Updated {Setting}", setting);
}
else
{
if (setting.EntityName != EntityNames.Visitor)
{
_logger.Log(LogLevel.Error, this, LogFunction.Update, "User Not Authorized To Update Setting {Setting}", setting);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
}
setting = null;
}
return setting;
@ -141,11 +154,14 @@ namespace Oqtane.Controllers
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Setting Deleted {Setting}", setting);
}
else
{
if (entityName != EntityNames.Visitor)
{
_logger.Log(LogLevel.Error, this, LogFunction.Delete, "User Not Authorized To Delete Setting {Setting}", setting);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
}
}
}
// DELETE api/<controller>/clear
[HttpDelete("clear")]
@ -219,6 +235,7 @@ namespace Oqtane.Controllers
authorized = User.IsInRole(RoleNames.Admin);
if (!authorized)
{
// a visitor may have cookies disabled
if (int.TryParse(Request.Cookies[_visitorCookie], out int visitorId))
{
authorized = (visitorId == entityId);

View File

@ -89,10 +89,10 @@ namespace Oqtane.Controllers
site.Pages = new List<Page>();
foreach (Page page in _pages.GetPages(site.SiteId))
{
if (_userPermissions.IsAuthorized(User, PermissionNames.View, page.Permissions))
if (_userPermissions.IsAuthorized(User, PermissionNames.View, page.PermissionList))
{
page.Settings = settings.Where(item => item.EntityId == page.PageId)
.Where(item => !item.IsPrivate || _userPermissions.IsAuthorized(User, PermissionNames.Edit, page.Permissions))
.Where(item => !item.IsPrivate || _userPermissions.IsAuthorized(User, PermissionNames.Edit, page.PermissionList))
.ToDictionary(setting => setting.SettingName, setting => setting.SettingValue);
site.Pages.Add(page);
}
@ -105,13 +105,13 @@ namespace Oqtane.Controllers
site.Modules = new List<Module>();
foreach (PageModule pagemodule in _pageModules.GetPageModules(site.SiteId))
{
if (_userPermissions.IsAuthorized(User, PermissionNames.View, pagemodule.Module.Permissions))
if (_userPermissions.IsAuthorized(User, PermissionNames.View, pagemodule.Module.PermissionList))
{
Module module = new Module();
module.SiteId = pagemodule.Module.SiteId;
module.ModuleDefinitionName = pagemodule.Module.ModuleDefinitionName;
module.AllPages = pagemodule.Module.AllPages;
module.Permissions = pagemodule.Module.Permissions;
module.PermissionList = pagemodule.Module.PermissionList;
module.CreatedBy = pagemodule.Module.CreatedBy;
module.CreatedOn = pagemodule.Module.CreatedOn;
module.ModifiedBy = pagemodule.Module.ModifiedBy;
@ -128,9 +128,10 @@ namespace Oqtane.Controllers
module.Order = pagemodule.Order;
module.ContainerType = pagemodule.ContainerType;
module.ModuleDefinition = moduledefinitions.Find(item => item.ModuleDefinitionName == module.ModuleDefinitionName);
module.ModuleDefinition = _moduleDefinitions.FilterModuleDefinition(moduledefinitions.Find(item => item.ModuleDefinitionName == module.ModuleDefinitionName));
module.Settings = settings.Where(item => item.EntityId == pagemodule.ModuleId)
.Where(item => !item.IsPrivate || _userPermissions.IsAuthorized(User, PermissionNames.Edit, pagemodule.Module.Permissions))
.Where(item => !item.IsPrivate || _userPermissions.IsAuthorized(User, PermissionNames.Edit, pagemodule.Module.PermissionList))
.ToDictionary(setting => setting.SettingName, setting => setting.SettingValue);
site.Modules.Add(module);

View File

@ -32,13 +32,23 @@ namespace Oqtane.Controllers
{
var results = new List<Dictionary<string, string>>();
Dictionary<string, string> row;
if (string.IsNullOrEmpty(sqlquery.DBType) || string.IsNullOrEmpty(sqlquery.DBConnectionString))
{
Tenant tenant = _tenants.GetTenant(sqlquery.TenantId);
if (tenant != null)
{
sqlquery.DBType = tenant.DBType;
sqlquery.DBConnectionString = tenant.DBConnectionString;
}
}
try
{
foreach (string query in sqlquery.Query.Split("GO", StringSplitOptions.RemoveEmptyEntries))
{
IDataReader dr = _sql.ExecuteReader(tenant, query);
_logger.Log(LogLevel.Information, this, LogFunction.Other, "Sql Query {Query} Executed on Tenant {TenantId}", query, sqlquery.TenantId);
IDataReader dr = _sql.ExecuteReader(sqlquery.DBType, sqlquery.DBConnectionString, query);
_logger.Log(LogLevel.Information, this, LogFunction.Other, "Sql Query {Query} Executed on Database {DBType} and Connection {DBConnectionString}", query, sqlquery.DBType, sqlquery.DBConnectionString);
while (dr.Read())
{
row = new Dictionary<string, string>();
@ -53,7 +63,7 @@ namespace Oqtane.Controllers
catch (Exception ex)
{
results.Add(new Dictionary<string, string>() { { "Error", ex.Message } });
_logger.Log(LogLevel.Warning, this, LogFunction.Other, "Sql Query {Query} Executed on Tenant {TenantId} Resulted In An Error {Error}", sqlquery.Query, sqlquery.TenantId, ex.Message);
_logger.Log(LogLevel.Warning, this, LogFunction.Other, "Sql Query {Query} Executed on Database {DBType} and Connection {DBConnectionString} Resulted In An Error {Error}", sqlquery.Query, sqlquery.DBType, sqlquery.DBConnectionString, ex.Message);
}
sqlquery.Results = results;
return sqlquery;

View File

@ -63,6 +63,12 @@ namespace Oqtane.Controllers
}
systeminfo.Add("Log", log);
break;
case "connectionstrings":
foreach (var kvp in _configManager.GetSettings(SettingKeys.ConnectionStringsSection))
{
systeminfo.Add(kvp.Key, kvp.Value);
}
break;
}
return systeminfo;
@ -84,16 +90,25 @@ namespace Oqtane.Controllers
{
foreach(KeyValuePair<string, object> kvp in settings)
{
_configManager.AddOrUpdateSetting(kvp.Key, kvp.Value, false);
UpdateSetting(kvp.Key, kvp.Value);
}
}
// PUT: api/<controller>
[HttpPut("{key}/{value}")]
[Authorize(Roles = RoleNames.Host)]
public void Put(string key, object value)
private void UpdateSetting(string key, object value)
{
switch (key.ToLower())
{
case "clearlog":
string path = Path.Combine(_environment.ContentRootPath, "Content", "Log", "error.log");
if (System.IO.File.Exists(path))
{
System.IO.File.Delete(path);
}
break;
default:
_configManager.AddOrUpdateSetting(key, value, false);
break;
}
}
}
}

View File

@ -330,11 +330,11 @@ namespace Oqtane.Controllers
// delete user
_users.DeleteUser(user.UserId);
_syncManager.AddSyncEvent(_tenantManager.GetAlias().TenantId, EntityNames.User, user.UserId, SyncEventActions.Delete);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "User Deleted {UserId}", user.UserId);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "User Deleted {UserId}", user.UserId, result.ToString());
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Delete, "Error Deleting User {UserId}", user.UserId, result.ToString());
_logger.Log(LogLevel.Error, this, LogFunction.Delete, "Error Deleting User {UserId}", user.UserId);
}
}
}

View File

@ -50,7 +50,7 @@ namespace Oqtane.Controllers
bool authorized = User.IsInRole(RoleNames.Admin);
if (!authorized)
{
var visitorCookie = "APP_VISITOR_" + _alias.SiteId.ToString();
var visitorCookie = Constants.VisitorCookiePrefix + _alias.SiteId.ToString();
if (int.TryParse(Request.Cookies[visitorCookie], out int visitorId))
{
authorized = (visitorId == id);

View File

@ -72,6 +72,7 @@ namespace Microsoft.Extensions.DependencyInjection
internal static IServiceCollection AddOqtaneTransientServices(this IServiceCollection services)
{
services.AddTransient<IDBContextDependencies, DBContextDependencies>();
services.AddTransient<ITenantManager, TenantManager>();
services.AddTransient<IAliasAccessor, AliasAccessor>();
services.AddTransient<IUserPermissions, UserPermissions>();

View File

@ -77,7 +77,7 @@ namespace Oqtane.Extensions
{
OnRedirectToIdentityProvider = context =>
{
foreach(var parameter in sitesettings.GetValue("ExternalLogin:Parameters", "").Split(","))
foreach (var parameter in sitesettings.GetValue("ExternalLogin:Parameters", "").Split(","))
{
context.ProtocolMessage.SetParameter(parameter.Split("=")[0], parameter.Split("=")[1]);
}
@ -197,7 +197,7 @@ namespace Oqtane.Extensions
}
// validate user
var identity = await ValidateUser(email, id, claims, context.HttpContext);
var identity = await ValidateUser(email, id, claims, context.HttpContext, context.Principal);
if (identity.Label == ExternalLoginStatus.Success)
{
identity.AddClaim(new Claim("access_token", context.AccessToken));
@ -232,7 +232,7 @@ namespace Oqtane.Extensions
var claims = string.Join(", ", context.Principal.Claims.Select(item => item.Type).ToArray());
// validate user
var identity = await ValidateUser(email, id, claims, context.HttpContext);
var identity = await ValidateUser(email, id, claims, context.HttpContext, context.Principal);
if (identity.Label == ExternalLoginStatus.Success)
{
// external roles
@ -278,7 +278,7 @@ namespace Oqtane.Extensions
return Task.CompletedTask;
}
private static async Task<ClaimsIdentity> ValidateUser(string email, string id, string claims, HttpContext httpContext)
private static async Task<ClaimsIdentity> ValidateUser(string email, string id, string claims, HttpContext httpContext, ClaimsPrincipal claimsPrincipal)
{
var _logger = httpContext.RequestServices.GetRequiredService<ILogManager>();
ClaimsIdentity identity = new ClaimsIdentity(Constants.AuthenticationScheme);
@ -358,13 +358,13 @@ namespace Oqtane.Extensions
else
{
identity.Label = ExternalLoginStatus.UserNotCreated;
_logger.Log(user.SiteId, LogLevel.Error, "ExternalLogin", Enums.LogFunction.Create, "Unable To Add User {Email}", email);
_logger.Log(alias.SiteId, LogLevel.Error, "ExternalLogin", Enums.LogFunction.Create, "Unable To Add User {Email}", email);
}
}
else
{
identity.Label = ExternalLoginStatus.UserNotCreated;
_logger.Log(user.SiteId, LogLevel.Error, "ExternalLogin", Enums.LogFunction.Create, "Unable To Add Identity User {Email} {Error}", email, result.Errors.ToString());
_logger.Log(alias.SiteId, LogLevel.Error, "ExternalLogin", Enums.LogFunction.Create, "Unable To Add Identity User {Email} {Error}", email, result.Errors.ToString());
}
}
else
@ -427,6 +427,55 @@ namespace Oqtane.Extensions
user.LastLoginOn = DateTime.UtcNow;
user.LastIPAddress = httpContext.Connection.RemoteIpAddress.ToString();
_users.UpdateUser(user);
// user profile claims
if (!string.IsNullOrEmpty(httpContext.GetSiteSettings().GetValue("ExternalLogin:ProfileClaimTypes", "")))
{
var _settings = httpContext.RequestServices.GetRequiredService<ISettingRepository>();
var _profiles = httpContext.RequestServices.GetRequiredService<IProfileRepository>();
var profiles = _profiles.GetProfiles(alias.SiteId).ToList();
foreach (var mapping in httpContext.GetSiteSettings().GetValue("ExternalLogin:ProfileClaimTypes", "").Split(',', StringSplitOptions.RemoveEmptyEntries))
{
if (mapping.Contains(":"))
{
var claim = claimsPrincipal.Claims.FirstOrDefault(item => item.Type == mapping.Split(":")[0]);
if (claim != null)
{
var profile = profiles.FirstOrDefault(item => item.Name == mapping.Split(":")[1]);
if (profile != null)
{
if (!string.IsNullOrEmpty(claim.Value))
{
var setting = _settings.GetSetting(EntityNames.User, user.UserId, profile.Name);
if (setting != null)
{
setting.SettingValue = claim.Value;
_settings.UpdateSetting(setting);
}
else
{
setting = new Setting { EntityName = EntityNames.User, EntityId = user.UserId, SettingName = profile.Name, SettingValue = claim.Value, IsPrivate = profile.IsPrivate };
_settings.AddSetting(setting);
}
}
}
else
{
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "The User Profile {ProfileName} Does Not Exist For The Site. Please Verify Your User Profile Definitions.", mapping.Split(":")[1]);
}
}
else
{
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "The User Profile Claim {ClaimType} Does Not Exist. The Valid Claims Are {Claims}.", mapping.Split(":")[0], claims);
}
}
else
{
_logger.Log(LogLevel.Error, "ExternalLogin", Enums.LogFunction.Security, "The User Profile Claim Mapping {Mapping} Is Not Specified Correctly. It Should Be In The Format 'ClaimType:ProfileName'.", mapping);
}
}
}
_logger.Log(LogLevel.Information, "ExternalLogin", Enums.LogFunction.Security, "External User Login Successful For {Username} Using Provider {Provider}", user.Username, providerName);
}
}

View File

@ -1,6 +1,5 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json;
using Oqtane.Models;
@ -8,59 +7,9 @@ namespace Oqtane.Extensions
{
public static class PermissionExtension
{
public static string EncodePermissions(this IEnumerable<Permission> permissionList)
public static string EncodePermissions(this IEnumerable<Permission> permissions)
{
List<PermissionString> permissionstrings = new List<PermissionString>();
string entityname = "";
string permissionname = "";
string permissions = "";
StringBuilder permissionsbuilder = new StringBuilder();
string securityid = "";
foreach (Permission permission in permissionList.OrderBy(item => item.EntityName).ThenBy(item => item.PermissionName))
{
// permission collections are grouped by entityname and permissionname
if (entityname != permission.EntityName || permissionname != permission.PermissionName)
{
permissions = permissionsbuilder.ToString();
if (permissions != "")
{
permissionstrings.Add(new PermissionString { EntityName = entityname, PermissionName = permissionname, Permissions = permissions.Substring(0, permissions.Length - 1) });
}
entityname = permission.EntityName;
permissionname = permission.PermissionName;
permissionsbuilder = new StringBuilder();
}
// deny permissions are prefixed with a "!"
string prefix = !permission.IsAuthorized ? "!" : "";
// encode permission
if (permission.UserId == null)
{
securityid = prefix + permission.Role.Name + ";";
}
else
{
securityid = prefix + "[" + permission.UserId + "];";
}
// insert deny permissions at the beginning and append grant permissions at the end
if (prefix == "!")
{
permissionsbuilder.Insert(0, securityid);
}
else
{
permissionsbuilder.Append(securityid);
}
}
permissions = permissionsbuilder.ToString();
if (permissions != "")
{
permissionstrings.Add(new PermissionString { EntityName = entityname, PermissionName = permissionname, Permissions = permissions.Substring(0, permissions.Length - 1) });
}
return JsonSerializer.Serialize(permissionstrings);
return JsonSerializer.Serialize(permissions);
}
}
}

View File

@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Nodes;
using Microsoft.Extensions.Configuration;
@ -42,6 +44,16 @@ namespace Oqtane.Infrastructure
return value;
}
public Dictionary<string, string> GetSettings(string sectionKey)
{
var settings = new Dictionary<string, string>();
foreach (var kvp in _config.GetSection(sectionKey).GetChildren().AsEnumerable())
{
settings.Add(kvp.Key, kvp.Value);
}
return settings;
}
public void AddOrUpdateSetting<T>(string key, T value, bool reload)
{
AddOrUpdateSetting("appsettings.json", key, value, reload);

View File

@ -62,7 +62,7 @@ namespace Oqtane.Infrastructure
}
catch (Exception ex)
{
result.Message = "Master Database Not Installed Correctly. " + ex.Message;
result.Message = "Master Database Not Installed Correctly. " + ex.ToString();
}
}
else // cannot connect
@ -74,7 +74,7 @@ namespace Oqtane.Infrastructure
}
catch (Exception ex)
{
result.Message = "Cannot Connect To Master Database. " + ex.Message;
result.Message = "Cannot Connect To Master Database. " + ex.ToString();
}
}
}
@ -162,6 +162,16 @@ namespace Oqtane.Infrastructure
{
install.DefaultContainer = GetInstallationConfig(SettingKeys.DefaultContainerKey, Constants.DefaultContainer);
}
// add new site
if (install.TenantName != TenantNames.Master && install.ConnectionString.Contains("="))
{
_configManager.AddOrUpdateSetting($"{SettingKeys.ConnectionStringsSection}:{install.TenantName}", install.ConnectionString, false);
}
if (install.TenantName == TenantNames.Master && !install.ConnectionString.Contains("="))
{
install.ConnectionString = _config.GetConnectionString(install.ConnectionString);
}
}
else
{
@ -247,7 +257,7 @@ namespace Oqtane.Infrastructure
}
catch (Exception ex)
{
result.Message = ex.Message;
result.Message = ex.ToString();
_filelogger.LogError(Utilities.LogMessage(this, result.Message));
}
@ -273,7 +283,7 @@ namespace Oqtane.Infrastructure
var database = Activator.CreateInstance(type) as IDatabase;
// create data directory if does not exist
var dataDirectory = AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString();
var dataDirectory = AppDomain.CurrentDomain.GetData(Constants.DataDirectory)?.ToString();
if (!Directory.Exists(dataDirectory)) Directory.CreateDirectory(dataDirectory ?? String.Empty);
var dbOptions = new DbContextOptionsBuilder().UseOqtaneDatabase(database, NormalizeConnectionString(install.ConnectionString)).Options;
@ -286,7 +296,7 @@ namespace Oqtane.Infrastructure
}
catch (Exception ex)
{
result.Message = "An Error Occurred Creating The Database. This Is Usually Related To Your User Not Having Sufficient Rights To Perform This Operation. Please Note That You Can Also Create The Database Manually Prior To Initiating The Install Wizard. " + ex.Message;
result.Message = "An Error Occurred Creating The Database. This Is Usually Related To Your User Not Having Sufficient Rights To Perform This Operation. Please Note That You Can Also Create The Database Manually Prior To Initiating The Install Wizard. " + ex.ToString();
_filelogger.LogError(Utilities.LogMessage(this, result.Message));
}
}
@ -316,10 +326,7 @@ namespace Oqtane.Infrastructure
using (var masterDbContext = new MasterDBContext(new DbContextOptions<MasterDBContext>(), null, _config))
{
if (installation.Success && (install.DatabaseType == Constants.DefaultDBType))
{
UpgradeSqlServer(sql, install.ConnectionString, install.DatabaseType, true);
}
AddEFMigrationsHistory(sql, install.ConnectionString, install.DatabaseType, "", true);
// push latest model into database
masterDbContext.Database.Migrate();
result.Success = true;
@ -327,7 +334,7 @@ namespace Oqtane.Infrastructure
}
catch (Exception ex)
{
result.Message = "An Error Occurred Provisioning The Master Database. This Is Usually Related To The Master Database Not Being In A Supported State. " + ex.Message;
result.Message = "An Error Occurred Provisioning The Master Database. This Is Usually Related To The Master Database Not Being In A Supported State. " + ex.ToString();
_filelogger.LogError(Utilities.LogMessage(this, result.Message));
}
}
@ -354,7 +361,7 @@ namespace Oqtane.Infrastructure
tenant = new Tenant
{
Name = install.TenantName,
DBConnectionString = DenormalizeConnectionString(install.ConnectionString),
DBConnectionString = (install.TenantName == TenantNames.Master) ? SettingKeys.ConnectionStringKey : install.TenantName,
DBType = install.DatabaseType,
CreatedBy = "",
CreatedOn = DateTime.UtcNow,
@ -370,7 +377,7 @@ namespace Oqtane.Infrastructure
tenant = db.Tenant.FirstOrDefault(item => item.Name == install.TenantName);
}
var aliasNames = install.Aliases.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(sValue => sValue.Trim()).ToArray();
var aliasNames = install.Aliases.Split(',', StringSplitOptions.RemoveEmptyEntries).Select(sValue => sValue.Trim()).ToArray();
var firstAlias = aliasNames[0];
foreach (var aliasName in aliasNames)
{
@ -406,28 +413,26 @@ namespace Oqtane.Infrastructure
{
var result = new Installation { Success = false, Message = string.Empty };
var versions = Constants.ReleaseVersions.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
var versions = Constants.ReleaseVersions.Split(',', StringSplitOptions.RemoveEmptyEntries);
using (var scope = _serviceScopeFactory.CreateScope())
{
var upgrades = scope.ServiceProvider.GetRequiredService<IUpgradeManager>();
var sql = scope.ServiceProvider.GetRequiredService<ISqlRepository>();
var tenantManager = scope.ServiceProvider.GetRequiredService<ITenantManager>();
var DBContextDependencies = scope.ServiceProvider.GetRequiredService<IDBContextDependencies>();
using (var db = GetInstallationContext())
{
foreach (var tenant in db.Tenant.ToList())
{
tenantManager.SetTenant(tenant.TenantId);
tenant.DBConnectionString = MigrateConnectionString(db, tenant);
try
{
using (var tenantDbContext = new TenantDBContext(tenantManager, null))
using (var tenantDbContext = new TenantDBContext(DBContextDependencies))
{
if (install.DatabaseType == Constants.DefaultDBType)
{
UpgradeSqlServer(sql, tenant.DBConnectionString, tenant.DBType, false);
}
AddEFMigrationsHistory(sql, _configManager.GetSetting($"{SettingKeys.ConnectionStringsSection}:{tenant.DBConnectionString}", ""), tenant.DBType, tenant.Version, false);
// push latest model into database
tenantDbContext.Database.Migrate();
result.Success = true;
@ -435,7 +440,7 @@ namespace Oqtane.Infrastructure
}
catch (Exception ex)
{
result.Message = "An Error Occurred Migrating A Tenant Database. This Is Usually Related To A Tenant Database Not Being In A Supported State. " + ex.Message;
result.Message = "An Error Occurred Migrating A Tenant Database. This Is Usually Related To A Tenant Database Not Being In A Supported State. " + ex.ToString();
_filelogger.LogError(Utilities.LogMessage(this, result.Message));
}
@ -456,7 +461,7 @@ namespace Oqtane.Infrastructure
}
catch (Exception ex)
{
result.Message = "An Error Occurred Executing Upgrade Logic. " + ex.Message;
result.Message = "An Error Occurred Executing Upgrade Logic. " + ex.ToString();
_filelogger.LogError(Utilities.LogMessage(this, result.Message));
}
}
@ -486,7 +491,7 @@ namespace Oqtane.Infrastructure
{
if (!string.IsNullOrEmpty(moduleDefinition.ReleaseVersions))
{
var versions = moduleDefinition.ReleaseVersions.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
var versions = moduleDefinition.ReleaseVersions.Split(',', StringSplitOptions.RemoveEmptyEntries);
using (var db = GetInstallationContext())
{
if (!string.IsNullOrEmpty(moduleDefinition.ServerManagerType))
@ -526,7 +531,7 @@ namespace Oqtane.Infrastructure
}
catch (Exception ex)
{
result.Message = "An Error Occurred Installing " + moduleDefinition.Name + " Version " + versions[i] + " - " + ex.Message;
result.Message = "An Error Occurred Installing " + moduleDefinition.Name + " Version " + versions[i] + " - " + ex.ToString();
}
}
}
@ -575,7 +580,7 @@ namespace Oqtane.Infrastructure
{
// set the alias explicitly so the tenant can be resolved
var aliases = scope.ServiceProvider.GetRequiredService<IAliasRepository>();
var aliasNames = install.Aliases.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(sValue => sValue.Trim()).ToArray();
var aliasNames = install.Aliases.Split(',', StringSplitOptions.RemoveEmptyEntries).Select(sValue => sValue.Trim()).ToArray();
var firstAlias = aliasNames[0];
var alias = aliases.GetAliases().FirstOrDefault(item => item.Name == firstAlias);
var tenantManager = scope.ServiceProvider.GetRequiredService<ITenantManager>();
@ -664,7 +669,7 @@ namespace Oqtane.Infrastructure
}
catch (Exception ex)
{
result.Message = "An Error Occurred Creating Site. " + ex.Message;
result.Message = "An Error Occurred Creating Site. " + ex.ToString();
}
}
@ -737,7 +742,7 @@ namespace Oqtane.Infrastructure
}
catch (Exception ex)
{
logger.Log(alias.SiteId, Shared.LogLevel.Error, "Site Migration", LogFunction.Other, "An Error Occurred Executing Site Migration {Type} For {Alias} And Version {Version} {Error}", upgrade.Value, alias.Name, version, ex.Message);
logger.Log(alias.SiteId, Shared.LogLevel.Error, "Site Migration", LogFunction.Other, ex, "An Error Occurred Executing Site Migration {Type} For {Alias} And Version {Version}", upgrade.Value, alias.Name, version);
}
}
}
@ -753,8 +758,8 @@ namespace Oqtane.Infrastructure
private string DenormalizeConnectionString(string connectionString)
{
var dataDirectory = AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString();
connectionString = connectionString.Replace(dataDirectory ?? String.Empty, "|DataDirectory|");
var dataDirectory = AppDomain.CurrentDomain.GetData(Constants.DataDirectory)?.ToString();
connectionString = connectionString.Replace(dataDirectory ?? String.Empty, $"|{Constants.DataDirectory}|");
return connectionString;
}
@ -780,8 +785,8 @@ namespace Oqtane.Infrastructure
private string NormalizeConnectionString(string connectionString)
{
var dataDirectory = AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString();
connectionString = connectionString.Replace("|DataDirectory|", dataDirectory);
var dataDirectory = AppDomain.CurrentDomain.GetData(Constants.DataDirectory)?.ToString();
connectionString = connectionString.Replace($"|{Constants.DataDirectory}|", dataDirectory);
return connectionString;
}
@ -799,7 +804,10 @@ namespace Oqtane.Infrastructure
_configManager.AddOrUpdateSetting($"{SettingKeys.DatabaseSection}:{SettingKeys.DatabaseTypeKey}", databaseType, true);
}
public void UpgradeSqlServer(ISqlRepository sql, string connectionString, string databaseType, bool isMaster)
public void AddEFMigrationsHistory(ISqlRepository sql, string connectionString, string databaseType, string version, bool isMaster)
{
// in version 2.1.0 the __EFMigrationsHistory tables were introduced and must be added to existing SQL Server installations
if ((isMaster || (version != null && Version.Parse(version).CompareTo(Version.Parse("2.1.0")) < 0)) && databaseType == Constants.DefaultDBType)
{
var script = (isMaster) ? "MigrateMaster.sql" : "MigrateTenant.sql";
@ -808,6 +816,28 @@ namespace Oqtane.Infrastructure
sql.ExecuteNonQuery(connectionString, databaseType, query);
}
}
public string MigrateConnectionString(InstallationContext db, Tenant tenant)
{
// migrate connection strings from the Tenant table to appsettings
if (tenant.DBConnectionString.Contains("="))
{
var defaultConnection = _configManager.GetConnectionString(SettingKeys.ConnectionStringKey);
if (tenant.DBConnectionString == defaultConnection)
{
tenant.DBConnectionString = SettingKeys.ConnectionStringKey;
}
else
{
_configManager.AddOrUpdateSetting($"{SettingKeys.ConnectionStringsSection}:{tenant.Name}", tenant.DBConnectionString, false);
tenant.DBConnectionString = tenant.Name;
}
db.Entry(tenant).State = EntityState.Modified;
db.SaveChanges();
}
return tenant.DBConnectionString;
}
private void ValidateConfiguration()
{

View File

@ -30,15 +30,12 @@ namespace Oqtane.Infrastructure
public void InstallPackages()
{
if (!InstallPackages(_environment.WebRootPath, _environment.ContentRootPath))
{
// error installing packages
}
InstallPackages(_environment.WebRootPath, _environment.ContentRootPath);
}
public static bool InstallPackages(string webRootPath, string contentRootPath)
{
bool install = false;
bool install = true;
string binPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
string sourceFolder = Path.Combine(contentRootPath, "Packages");
@ -81,6 +78,8 @@ namespace Oqtane.Infrastructure
// iterate through Nuget packages in source folder
foreach (string packagename in Directory.GetFiles(sourceFolder, "*.nupkg"))
{
try
{
// iterate through files
using (ZipArchive archive = ZipFile.OpenRead(packagename))
@ -170,10 +169,16 @@ namespace Oqtane.Infrastructure
}
}
}
}
catch (Exception ex)
{
// problem installing package - logging is not possible as this is a static method
Debug.WriteLine($"Oqtane Error: Installing Package {packagename} - {ex}");
install = false;
}
// remove package
File.Delete(packagename);
install = true;
}
return install;

View File

@ -1,3 +1,4 @@
using System.Collections.Generic;
using Microsoft.Extensions.Configuration;
namespace Oqtane.Infrastructure
@ -7,6 +8,7 @@ namespace Oqtane.Infrastructure
public IConfigurationSection GetSection(string sectionKey);
public T GetSetting<T>(string settingKey, T defaultValue);
public T GetSetting<T>(string sectionKey, string settingKey, T defaultValue);
public Dictionary<string, string> GetSettings(string sectionKey);
void AddOrUpdateSetting<T>(string key, T value, bool reload);
void AddOrUpdateSetting<T>(string file, string key, T value, bool reload);
void RemoveSetting(string key, bool reload);

View File

@ -138,7 +138,7 @@ namespace Oqtane.Infrastructure
try
{
client.Send(mailMessage);
sent = sent++;
sent++;
notification.IsDelivered = true;
notification.DeliveredOn = DateTime.UtcNow;
notificationRepository.UpdateNotification(notification);

View File

@ -43,10 +43,10 @@ namespace Oqtane.Infrastructure
});
context.Items.Add(Constants.HttpContextSiteSettingsKey, sitesettings);
// rewrite path by removing alias path prefix from api and pages requests (for consistent routing)
// rewrite path by removing alias path prefix from reserved route (api,pages,files) requests for consistent routes
if (!string.IsNullOrEmpty(alias.Path))
{
if (path.StartsWith("/" + alias.Path) && (path.Contains("/api/") || path.Contains("/pages/")))
if (path.StartsWith("/" + alias.Path) && (Constants.ReservedRoutes.Any(item => path.Contains("/" + item + "/"))))
{
context.Request.Path = path.Replace("/" + alias.Path, "");
}

View File

@ -45,40 +45,40 @@ namespace Oqtane.SiteTemplates
Icon = "oi oi-home",
IsNavigation = true,
IsPersonalizable = false,
PagePermissions = new List<Permission> {
PermissionList = new List<Permission> {
new Permission(PermissionNames.View, RoleNames.Everyone, true),
new Permission(PermissionNames.View, RoleNames.Admin, true),
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
}.EncodePermissions() ,
},
PageTemplateModules = new List<PageTemplateModule> {
new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "Welcome To Oqtane...", Pane = PaneNames.Default,
ModulePermissions = new List<Permission> {
PermissionList = new List<Permission> {
new Permission(PermissionNames.View, RoleNames.Everyone, true),
new Permission(PermissionNames.View, RoleNames.Admin, true),
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
}.EncodePermissions(),
},
Content = "<p><a href=\"https://www.oqtane.org\" target=\"_new\">Oqtane</a> is an open source <b>modular application framework</b> that provides advanced functionality for developing web, mobile, and desktop applications on .NET Core. It leverages the Blazor component model to compose a <b>fully dynamic</b> web development experience which can be hosted either client-side or server-side. Whether you are looking for a platform to <b>accelerate your web development</b> efforts, or simply interested in exploring the anatomy of a large-scale Blazor application, Oqtane provides a solid foundation based on proven enterprise architectural principles.</p>" +
"<p align=\"center\"><a href=\"https://www.oqtane.org\" target=\"_new\"><img class=\"img-fluid\" src=\"oqtane-glow.png\"></a></p><p align=\"center\"><a class=\"btn btn-primary\" href=\"https://www.oqtane.org/Community\" target=\"_new\">Join Our Community</a>&nbsp;&nbsp;<a class=\"btn btn-primary\" href=\"https://github.com/oqtane/oqtane.framework\" target=\"_new\">Clone Our Repo</a></p>" +
"<p><a href=\"https://dotnet.microsoft.com/apps/aspnet/web-apps/blazor\" target=\"_new\">Blazor</a> is an open source and cross-platform web UI framework for building single-page applications using .NET and C#. Blazor applications can be hosted in a variety of ways. Blazor Server uses SignalR (WebSockets) to host your application on a web server and provide a responsive and robust development experience. Blazor WebAssembly relies on Wasm, an open web standard that does not require plugins in order for applications to run natively in a web browser. Blazor Hybrid is part of .NET MAUI and uses a Web View to render components natively on mobile and desktop devices. Razor components can be used with all of the hosting models without any modification.</p>" +
"<p>Blazor is a feature of <a href=\"https://dotnet.microsoft.com/apps/aspnet\" target=\"_new\">.NET Core</a>, the popular cross platform web development framework from Microsoft that extends the <a href=\"https://dotnet.microsoft.com/learn/dotnet/what-is-dotnet\" target=\"_new\" >.NET developer platform</a> with tools and libraries for building web apps.</p>"
},
new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "MIT License", Pane = PaneNames.Default,
ModulePermissions = new List<Permission> {
PermissionList = new List<Permission> {
new Permission(PermissionNames.View, RoleNames.Everyone, true),
new Permission(PermissionNames.View, RoleNames.Admin, true),
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
}.EncodePermissions(),
},
Content = "<p>Copyright (c) 2018-2023 .NET Foundation</p>" +
"<p>Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:</p>" +
"<p>The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.</p>" +
"<p>THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.</p>"
},
new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "Secure Content", Pane = PaneNames.Default,
ModulePermissions = new List<Permission> {
PermissionList = new List<Permission> {
new Permission(PermissionNames.View, RoleNames.Registered, true),
new Permission(PermissionNames.View, RoleNames.Admin, true),
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
}.EncodePermissions(),
},
Content = "<p>Oqtane allows you to control access to your content using security roles. This module is only visible to Registered Users of the site.</p>"
}
}
@ -92,18 +92,18 @@ namespace Oqtane.SiteTemplates
Icon = "oi oi-lock-locked",
IsNavigation = true,
IsPersonalizable = false,
PagePermissions = new List<Permission> {
PermissionList = new List<Permission> {
new Permission(PermissionNames.View, RoleNames.Registered, true),
new Permission(PermissionNames.View, RoleNames.Admin, true),
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
}.EncodePermissions(),
},
PageTemplateModules = new List<PageTemplateModule> {
new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "Secure Content", Pane = PaneNames.Default,
ModulePermissions = new List<Permission> {
PermissionList = new List<Permission> {
new Permission(PermissionNames.View, RoleNames.Registered, true),
new Permission(PermissionNames.View, RoleNames.Admin, true),
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
}.EncodePermissions(),
},
Content = "<p>Oqtane allows you to control access to your content using security roles. This page is only visible to Registered Users of the site.</p>"
}
}
@ -117,18 +117,18 @@ namespace Oqtane.SiteTemplates
Icon = "oi oi-target",
IsNavigation = true,
IsPersonalizable = true,
PagePermissions = new List<Permission> {
PermissionList = new List<Permission> {
new Permission(PermissionNames.View, RoleNames.Everyone, true),
new Permission(PermissionNames.View, RoleNames.Admin, true),
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
}.EncodePermissions(),
},
PageTemplateModules = new List<PageTemplateModule> {
new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "My Page", Pane = PaneNames.Default,
ModulePermissions = new List<Permission> {
PermissionList = new List<Permission> {
new Permission(PermissionNames.View, RoleNames.Everyone, true),
new Permission(PermissionNames.View, RoleNames.Admin, true),
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
}.EncodePermissions(),
},
Content = "<p>Oqtane offers native support for user personalized pages. If a page is identified as personalizable by the site administrator in the page settings, when an authenticated user visits the page they will see an edit button at the top right corner of the page next to their username. When they click this button the sytem will create a new version of the page and allow them to edit the page content.</p>"
}
}
@ -142,23 +142,23 @@ namespace Oqtane.SiteTemplates
Icon = "oi oi-wrench",
IsNavigation = true,
IsPersonalizable = true,
PagePermissions = new List<Permission> {
PermissionList = new List<Permission> {
new Permission(PermissionNames.View, RoleNames.Host, true),
new Permission(PermissionNames.Edit, RoleNames.Host, true)
}.EncodePermissions(),
},
PageTemplateModules = new List<PageTemplateModule> {
new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "Software Development", Pane = PaneNames.Default,
ModulePermissions = new List<Permission> {
PermissionList = new List<Permission> {
new Permission(PermissionNames.View, RoleNames.Host, true),
new Permission(PermissionNames.Edit, RoleNames.Host, true)
}.EncodePermissions(),
},
Content = "<p>Oqtane offers a Module Creator which allows you to create new modules to extend the framework with additional capabilities. Simply provide some basic information and the system will scaffold a completely functional module which includes all of the necessary code files and assets to get you up and running as quickly as possible.</p>"
},
new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.ModuleCreator, Oqtane.Client", Title = "Module Creator", Pane = PaneNames.Default,
ModulePermissions = new List<Permission> {
PermissionList = new List<Permission> {
new Permission(PermissionNames.View, RoleNames.Host, true),
new Permission(PermissionNames.Edit, RoleNames.Host, true)
}.EncodePermissions()
}
}
}
});

View File

@ -32,11 +32,11 @@ namespace Oqtane.SiteTemplates
Icon = "oi oi-home",
IsNavigation = true,
IsPersonalizable = false,
PagePermissions = new List<Permission> {
PermissionList = new List<Permission> {
new Permission(PermissionNames.View, RoleNames.Everyone, true),
new Permission(PermissionNames.View, RoleNames.Admin, true),
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
}.EncodePermissions(),
},
PageTemplateModules = new List<PageTemplateModule>()
});

View File

@ -37,7 +37,7 @@ namespace Oqtane.Infrastructure
{
// legacy support for client api requests which would include the alias as a path prefix ( ie. {alias}/api/[controller] )
int aliasId;
string[] segments = httpcontext.Request.Path.Value.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
string[] segments = httpcontext.Request.Path.Value.Split('/', StringSplitOptions.RemoveEmptyEntries);
if (segments.Length > 1 && Shared.Constants.ReservedRoutes.Contains(segments[1]) && int.TryParse(segments[0], out aliasId))
{
alias = _aliasRepository.GetAliases().ToList().FirstOrDefault(item => item.AliasId == aliasId);

View File

@ -140,21 +140,21 @@ namespace Oqtane.Infrastructure
Icon = Icons.LinkBroken,
IsNavigation = true,
IsPersonalizable = false,
PagePermissions = new List<Permission>
PermissionList = new List<Permission>
{
new Permission(PermissionNames.View, RoleNames.Admin, true),
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
}.EncodePermissions(),
},
PageTemplateModules = new List<PageTemplateModule>
{
new PageTemplateModule
{
ModuleDefinitionName = typeof(Oqtane.Modules.Admin.UrlMappings.Index).ToModuleDefinitionName(), Title = "Url Mappings", Pane = PaneNames.Default,
ModulePermissions = new List<Permission>
PermissionList = new List<Permission>
{
new Permission(PermissionNames.View, RoleNames.Admin, true),
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
}.EncodePermissions(),
},
Content = ""
}
}
@ -169,21 +169,21 @@ namespace Oqtane.Infrastructure
Icon = Icons.Eye,
IsNavigation = true,
IsPersonalizable = false,
PagePermissions = new List<Permission>
PermissionList = new List<Permission>
{
new Permission(PermissionNames.View, RoleNames.Admin, true),
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
}.EncodePermissions(),
},
PageTemplateModules = new List<PageTemplateModule>
{
new PageTemplateModule
{
ModuleDefinitionName = typeof(Oqtane.Modules.Admin.Visitors.Index).ToModuleDefinitionName(), Title = "Visitor Management", Pane = PaneNames.Default,
ModulePermissions = new List<Permission>
PermissionList = new List<Permission>
{
new Permission(PermissionNames.View, RoleNames.Admin, true),
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
}.EncodePermissions(),
},
Content = ""
}
}
@ -217,20 +217,20 @@ namespace Oqtane.Infrastructure
Icon = Icons.X,
IsNavigation = false,
IsPersonalizable = false,
PagePermissions = new List<Permission>
PermissionList = new List<Permission>
{
new Permission(PermissionNames.View, RoleNames.Everyone, true),
new Permission(PermissionNames.View, RoleNames.Admin, true),
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
}.EncodePermissions(),
},
PageTemplateModules = new List<PageTemplateModule>
{
new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "Not Found", Pane = PaneNames.Default,
ModulePermissions = new List<Permission> {
PermissionList = new List<Permission> {
new Permission(PermissionNames.View, RoleNames.Everyone, true),
new Permission(PermissionNames.View, RoleNames.Admin, true),
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
}.EncodePermissions(),
},
Content = "<p>The page you requested does not exist.</p>"
}
}

View File

@ -122,6 +122,26 @@ namespace Oqtane.Migrations.EntityBuilders
return table.Column<DateTimeOffset>(name: RewriteName(name), nullable: nullable, defaultValue: defaultValue);
}
public void AddByteColumn(string name, bool nullable = false)
{
_migrationBuilder.AddColumn<byte>(RewriteName(name), RewriteName(EntityTableName), nullable: nullable, schema: Schema);
}
public void AddByteColumn(string name, bool nullable, int defaultValue)
{
_migrationBuilder.AddColumn<byte>(RewriteName(name), RewriteName(EntityTableName), nullable: nullable, defaultValue: defaultValue, schema: Schema);
}
protected OperationBuilder<AddColumnOperation> AddByteColumn(ColumnsBuilder table, string name, bool nullable = false)
{
return table.Column<byte>(name: RewriteName(name), nullable: nullable);
}
protected OperationBuilder<AddColumnOperation> AddByteColumn(ColumnsBuilder table, string name, bool nullable, int defaultValue)
{
return table.Column<byte>(name: RewriteName(name), nullable: nullable, defaultValue: defaultValue);
}
public void AddIntegerColumn(string name, bool nullable = false)
{
_migrationBuilder.AddColumn<int>(RewriteName(name), RewriteName(EntityTableName), nullable: nullable, schema: Schema);

View File

@ -0,0 +1,35 @@
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Oqtane.Databases.Interfaces;
using Oqtane.Migrations.EntityBuilders;
using Oqtane.Repository;
namespace Oqtane.Migrations.Tenant
{
[DbContext(typeof(TenantDBContext))]
[Migration("Tenant.03.03.02.01")]
public class AddFolderFileIsDeletedColumns : MultiDatabaseMigration
{
public AddFolderFileIsDeletedColumns(IDatabase database) : base(database)
{
}
protected override void Up(MigrationBuilder migrationBuilder)
{
// IsDeleted columns were removed in 3.2.2 however SQLite does not support column removal so they had to be restored
if (ActiveDatabase.Name != "Sqlite")
{
var folderEntityBuilder = new FolderEntityBuilder(migrationBuilder, ActiveDatabase);
folderEntityBuilder.AddBooleanColumn("IsDeleted", true);
var fileEntityBuilder = new FileEntityBuilder(migrationBuilder, ActiveDatabase);
fileEntityBuilder.AddBooleanColumn("IsDeleted", true);
}
}
protected override void Down(MigrationBuilder migrationBuilder)
{
// not implemented
}
}
}

View File

@ -2,7 +2,6 @@ using Oqtane.Infrastructure;
using Oqtane.Models;
using Oqtane.Modules.HtmlText.Repository;
using System.Net;
using Microsoft.AspNetCore.Http;
using Oqtane.Enums;
using Oqtane.Repository;
using Oqtane.Shared;
@ -17,15 +16,13 @@ namespace Oqtane.Modules.HtmlText.Manager
public class HtmlTextManager : MigratableModuleBase, IInstallable, IPortable
{
private readonly IHtmlTextRepository _htmlText;
private readonly ITenantManager _tenantManager;
private readonly IHttpContextAccessor _accessor;
private readonly IDBContextDependencies _DBContextDependencies;
private readonly ISqlRepository _sqlRepository;
public HtmlTextManager(IHtmlTextRepository htmlText, ITenantManager tenantManager, IHttpContextAccessor httpContextAccessor, ISqlRepository sqlRepository)
public HtmlTextManager(IHtmlTextRepository htmlText, IDBContextDependencies DBContextDependencies, ISqlRepository sqlRepository)
{
_htmlText = htmlText;
_tenantManager = tenantManager;
_accessor = httpContextAccessor;
_DBContextDependencies = DBContextDependencies;
_sqlRepository = sqlRepository;
}
@ -56,12 +53,12 @@ namespace Oqtane.Modules.HtmlText.Manager
// version 1.0.0 used SQL scripts rather than migrations, so we need to seed the migration history table
_sqlRepository.ExecuteNonQuery(tenant, MigrationUtils.BuildInsertScript("HtmlText.01.00.00.00"));
}
return Migrate(new HtmlTextContext(_tenantManager, _accessor), tenant, MigrationType.Up);
return Migrate(new HtmlTextContext(_DBContextDependencies), tenant, MigrationType.Up);
}
public bool Uninstall(Tenant tenant)
{
return Migrate(new HtmlTextContext(_tenantManager, _accessor), tenant, MigrationType.Down);
return Migrate(new HtmlTextContext(_DBContextDependencies), tenant, MigrationType.Down);
}
}
}

View File

@ -13,7 +13,7 @@ namespace Oqtane.Modules.HtmlText.Repository
[PrivateApi("Mark HtmlText classes as private, since it's not very useful in the public docs")]
public class HtmlTextContext : DBContextBase, ITransientService, IMultiDatabase
{
public HtmlTextContext(ITenantManager tenantManager, IHttpContextAccessor httpContextAccessor) : base(tenantManager, httpContextAccessor) { }
public HtmlTextContext(IDBContextDependencies DBContextDependencies) : base(DBContextDependencies) { }
public virtual DbSet<Models.HtmlText> HtmlText { get; set; }
}

View File

@ -1,10 +1,10 @@
using Oqtane.Models;
using Oqtane.Models;
namespace Oqtane.Modules
{
public interface IPortable
{
// You Must Set The "ServerAssemblyName" In Your IModule Interface
// You Must Set The "ServerManagerType" In Your IModule Interface
string ExportModule(Module module);

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