Compare commits

...

317 Commits

Author SHA1 Message Date
07ae0edd30 Merge pull request #879 from sbwalker/master
removed unnecessary file
2020-11-11 10:24:39 -05:00
32e8052079 removed unnecessary file 2020-11-11 10:24:10 -05:00
60ccad3106 Merge pull request #878 from sbwalker/master
fixed regression issue which was preventing proper handling of situations where module assembly is missing
2020-11-11 10:19:40 -05:00
bcc00a2dbb fixed regression issue which was preventing proper handling of situations where module assembly is missing 2020-11-11 10:19:04 -05:00
5080040590 Merge pull request #65 from oqtane/master
sync
2020-11-11 09:02:02 -05:00
0d71a3878f Merge pull request #877 from jimspillane/UpgradeExternalModuleTemplateToNET50
Upgrade external module template to net50
2020-11-11 09:00:36 -05:00
82b16d28ff Upgrade External Module Template to NET5.0 2020-11-10 21:02:51 -05:00
e34ffb716d Merge pull request #20 from oqtane/master
sync master
2020-11-10 20:52:37 -05:00
6f8c2fb2ed Merge pull request #875 from hishamco/net5
Update README.md
2020-11-10 16:29:21 -05:00
3970722abf Merge pull request #876 from sbwalker/master
include syncevents on module add/update/delete to trigger reload in UI
2020-11-10 16:27:37 -05:00
880ad0486a include syncevents on module add/update/delete to trigger reload in UI 2020-11-10 16:26:50 -05:00
a02786b8b0 Update README.md 2020-11-11 00:22:07 +03:00
2bea59fc66 Merge pull request #874 from hishamco/net5
Upgrade packages to .NET 5 RTM
2020-11-10 16:15:20 -05:00
45819aae07 Upgrade packages to .NET 5 RTM 2020-11-10 23:47:43 +03:00
4762fac0ce Merge pull request #873 from sbwalker/master
improve user experence of Module Creator during app restart
2020-11-09 15:53:14 -05:00
8b97872100 improve user experence of Module Creator during app restart 2020-11-09 15:52:40 -05:00
b921ec24ab Merge pull request #872 from sbwalker/master
fixed issue where modulestate was being modified and not treated as a readonly cache
2020-11-09 15:36:36 -05:00
07519bccde fixed issue where modulestate was being modified and not treated as a readonly cache 2020-11-09 15:35:32 -05:00
a8932ec1e0 Merge pull request #64 from oqtane/master
sync
2020-11-09 10:51:23 -05:00
a9bb82347d Merge pull request #871 from sbwalker/master
Removed comment and added defensive logic in the eent that the moduletype is not valid. Also changed default behavior to display the name if the key is missing. Will need an option in the future to enable the display of missing keys.
2020-11-09 09:43:52 -05:00
2c58a97ec1 fixed malformed tokens in external module template 2020-11-09 09:42:01 -05:00
41b30bfab2 Removed comment and added defensive logic in the eent that the moduletype is not valid. Also changed default behavior to display the name if the key is missing. Will need an option in the future to enable the display of missing keys. 2020-11-09 09:27:52 -05:00
a77aa5fe18 Merge pull request #63 from oqtane/master
sync
2020-11-09 08:29:14 -05:00
780b0c704c Merge pull request #870 from hishamco/localization
Use IStringLocalizerFactory for simplicity
2020-11-08 14:58:42 -05:00
26c054c22d Use IStringLocalizerFactory for simplicity 2020-11-08 22:26:04 +03:00
047336ad21 Merge pull request #62 from oqtane/master
sync
2020-11-08 10:52:36 -05:00
2d344975ff Merge pull request #869 from sbwalker/master
Module Creator should only include Framework References for 2.0.0 and above
2020-11-07 16:45:30 -05:00
e0017065af Module Creator should only include Framework References for 2.0.0 and above 2020-11-07 16:44:41 -05:00
f0a9739b37 Merge pull request #868 from sbwalker/master
upgrade Module Creator external template to .NET 5 RC2 for testing. In order to use it you must choose Local as the Target Version in the Module Creator.
2020-11-07 16:36:48 -05:00
5e801bd5ee upgrade Module Creator external template to .NET 5 RC2 for testing. In order to use it you must choose Local as the Target Version in the Module Creator. 2020-11-07 16:35:44 -05:00
824df0e849 Update README.md 2020-11-05 10:27:20 -05:00
a24fd099c0 Update README.md 2020-11-05 09:25:46 -05:00
c6fdc99690 Merge pull request #866 from sbwalker/master
move version to 2.0.0 and target to net5.0
2020-11-05 09:20:59 -05:00
b602113cd1 move version to 2.0.0 2020-11-05 09:19:56 -05:00
b6886116ad Merge pull request #865 from sbwalker/master
fixed JavaScript issue when loading scripts dynamically which caused only the first script to be loaded for a module
2020-11-05 08:51:58 -05:00
f976910730 fixed JavaScript issue when loading scripts dynamically which caused only the first script to be loaded 2020-11-05 08:50:52 -05:00
6726a42406 Merge pull request #864 from sbwalker/master
resolve Login UI issue #794
2020-11-04 17:44:09 -05:00
23a35cf3c4 resolve Login UI issue #794 2020-11-04 17:43:27 -05:00
f5b99bf9d1 Merge pull request #863 from sbwalker/master
implement optional Security parameter for TabPanel #797
2020-11-04 17:27:58 -05:00
d7135ad4f9 implement optional Security parameter for TabPanel #797 2020-11-04 17:27:15 -05:00
0d03f59cd4 Merge pull request #862 from sbwalker/master
use logo with glow effect so it is visible on both black and white backgrounds
2020-11-04 16:59:03 -05:00
5b49e1bc7c use logo with glow effect so it is visible on both black and white backgrounds 2020-11-04 16:58:02 -05:00
a3215e286a Merge pull request #61 from oqtane/master
sync
2020-11-04 16:00:53 -05:00
b10140f513 Merge pull request #861 from sbwalker/master
performance optimization to reduce calls from client to server unless content is changed
2020-11-04 15:59:49 -05:00
8eaa298d8b performance optimization to reduce calls from client to server unless content is changed 2020-11-04 15:58:13 -05:00
c52d255a30 Merge pull request #860 from sbwalker/master
load module settings automatically so that they are part of the ModuleState and can be easily accessed by developers
2020-11-04 15:41:48 -05:00
97cec46ec1 load module settings automatically so that they are part of the ModuleState and can be easily accessed by developers 2020-11-04 15:40:57 -05:00
46c1564e3d Merge pull request #859 from sbwalker/master
optimize performance when running on WebAssembly by caching assembly payload that needs to be served to new clients
2020-11-04 08:11:03 -05:00
700b6e2d68 optimize performance when running on WebAssembly by caching assembly payload that needs to be served to new clients 2020-11-04 08:10:21 -05:00
3cfb27b2bc Merge pull request #857 from sbwalker/master
fixed compatibility issue in .NET5/WebAssembly where assemblies were not being loaded into the default AppDomain, optimized service registration on WebAssembly, fixed spelling mistake for satellite assemblies constant and fixed issue in LocalizableComponent
2020-11-03 14:44:23 -05:00
b4b73b7e5a fixed compatibility issue in .NET5/WebAssembly where assemblies were not being loaded into the default AppDomain, optimized service registration on WebAssembly, fixed spelling mistake for satellite assemblies constant and fixed issue in LocalizableComponent 2020-11-03 14:41:49 -05:00
8c2338e590 Update README.md 2020-10-28 07:54:35 -04:00
89875516e6 Update README.md 2020-10-25 21:07:56 -04:00
15aa2c2e47 Merge pull request #60 from oqtane/master
sync
2020-10-25 13:11:01 -04:00
1bf4bd0022 Merge pull request #850 from hishamco/patch-1
Update README.md
2020-10-25 13:09:23 -04:00
570b885c09 Merge pull request #849 from hishamco/section-localization
Localize section component
2020-10-25 13:08:40 -04:00
90de3949d2 Merge pull request #848 from hishamco/tab-panel-localization
Localize TabPanel component
2020-10-25 13:08:20 -04:00
38eb7c05b3 Update README.md 2020-10-24 10:12:48 +03:00
5948e7ba76 Localize section component 2020-10-24 10:05:57 +03:00
3cbf55e1ce Move DisplayHeading() from TabStrip to TabPanel 2020-10-24 09:52:17 +03:00
ec270fbff0 Localize TabPanel component 2020-10-24 09:44:56 +03:00
03926bccb0 Merge pull request #846 from sbwalker/master
fix folder parsing issue specific to Azure environment where WebRootPath contains 2 wwwroot nested folders
2020-10-22 16:31:41 -04:00
cdb7de84fa fix folder parsing issue specific to Azure environment where WebRootPath contains 2 wwwroot nested folders 2020-10-22 16:30:27 -04:00
76eccececa Update README.md 2020-10-21 09:20:47 -04:00
2f17945020 Merge pull request #843 from sbwalker/master
Fixed issue where Page Url expansion script for 1.0.4 was not implemented properly - it was not tagged as an embedded resource.
2020-10-21 08:02:57 -04:00
cbd7caa6df Merge branch 'master' of https://github.com/sbwalker/oqtane.framework into master 2020-10-21 08:01:39 -04:00
0ef04e81ff Fixed issue where Page Url expansion script for 1.0.4 was not implemented properly - it was not tagged as an embedded resource. 2020-10-21 08:01:17 -04:00
4dbe16bee5 Merge pull request #841 from hishamco/action-link-localization
Localize ActionDialog
2020-10-20 16:47:34 -04:00
3f78c99ed4 Localize ActionDialog 2020-10-20 23:16:53 +03:00
eaa8649699 Merge pull request #59 from oqtane/master
sync
2020-10-20 12:54:28 -04:00
5835e037a7 Merge pull request #839 from hishamco/action-link-localization
Fix issue with ActionLink localization
2020-10-20 12:50:42 -04:00
af41e8bcfb Fix issue with ActionLink localization 2020-10-20 18:36:06 +03:00
d19f3f358b Update README.md 2020-10-20 09:13:43 -04:00
fc7000394f Merge pull request #58 from oqtane/master
sync
2020-10-20 09:05:32 -04:00
892b79833b Merge pull request #837 from mikecasas/master
Deleted some white space.
2020-10-20 07:53:58 -04:00
ef51d5f05d Merge pull request #836 from TonyValenti/master
Factor out Policy Names
2020-10-20 07:53:19 -04:00
40d8315cff Merge pull request #834 from hishamco/lang
Remove LangVersion
2020-10-20 07:52:55 -04:00
544475c489 Merge pull request #828 from hishamco/action-link-localization
Action link localization
2020-10-20 07:52:40 -04:00
e61cd3d366 Merge pull request #824 from hishamco/localizable-component
Localizable component
2020-10-20 07:52:11 -04:00
01fc60d35d Merge remote-tracking branch 'upstream/master' into master 2020-10-20 07:32:33 -04:00
17a9710c14 Delete white space. 2020-10-20 07:31:24 -04:00
8cf846ba90 Factor out Policy Names
Change AppDomain to AppContext
2020-10-19 20:04:13 -05:00
70a345d2a9 Remove LangVersion 2020-10-19 22:11:51 +03:00
79b584f268 Check needed if the component is localizable 2020-10-19 21:47:59 +03:00
3b09699618 Merge pull request #831 from mikecasas/master
Update template to use the new constants.
2020-10-19 09:48:32 -04:00
1315e0382e Update template to use the new constants. 2020-10-19 09:26:28 -04:00
c733707adc Merge pull request #830 from sbwalker/master
modify comment
2020-10-19 08:51:21 -04:00
9294537e23 modify comment 2020-10-19 08:50:37 -04:00
2abc2cdf20 Merge pull request #829 from sbwalker/master
introduce Resource Declaration and Location properties to offer more resource management options for developers
2020-10-19 08:04:00 -04:00
ecacb681b4 introduce Resource Declaration and Location properties to offer more resource management options for developers 2020-10-19 08:03:04 -04:00
34b9903b15 Localize ActionLink 2020-10-19 12:16:35 +03:00
fed56098a0 Avoid resource check in child components 2020-10-19 11:36:05 +03:00
fd5d777d3a Label should use LocalizableComponent 2020-10-19 11:17:34 +03:00
5bb7c63d44 Introduce LocalizableComponent 2020-10-19 11:16:46 +03:00
34122a344d Merge pull request #818 from sbwalker/master
upgrade to .NET5 RC2
2020-10-18 09:31:16 -04:00
74026401a6 upgrade to .NET5 RC2 2020-10-18 09:30:24 -04:00
f5beb54ddb Merge pull request #817 from sbwalker/master
fixed compilation warnings in AuditInfo, fixed issue in ModuleMessage triggered in InstallWizard, fixed PWA JavaScript in ThemeBuilder for all browsers
2020-10-18 09:10:06 -04:00
d082c5427b fixed compilation warnings in AuditInfo, fixed issue in ModuleMessage triggered in InstallWizard, fixed PWA JavaScript in ThemeBuilder for all browsers 2020-10-18 09:09:18 -04:00
f3e2177423 Merge pull request #57 from oqtane/master
sync
2020-10-18 08:54:34 -04:00
7c8beac3dc Merge pull request #788 from sbwalker/master
Changes for .NET 5
2020-10-18 08:53:11 -04:00
1293b98226 Merge pull request #801 from hishamco/localizable-labels
Use ServiceActivator instead of IHttpContextAccessor
2020-10-18 08:52:13 -04:00
63f43bc27e Merge pull request #802 from iJungleboy/master
Add Documentation attributes #570
2020-10-18 08:52:01 -04:00
f9296ec5c5 Merge pull request #816 from hishamco/editor-config
Add .editorconfig
2020-10-18 08:51:48 -04:00
9543cd7031 Merge pull request #805 from TonyValenti/master
Factored out Contants.*** Role into RoleNames.***
2020-10-18 08:51:23 -04:00
857d699c0d Add .editorconfig 2020-10-18 00:55:16 +03:00
c683de2cda Refactor TenantNames.Master 2020-10-16 10:45:13 -05:00
3e71bdfef3 Replace string with System.Net.Mime.MediaTypeNames.Application.Octet 2020-10-16 10:38:19 -05:00
766be6c929 Factor out default controller route. 2020-10-16 10:37:17 -05:00
f33fb4d001 Factoring out Constants.AdminPane and Constants.HostUser 2020-10-16 10:23:17 -05:00
becc779db8 Extracted "ViewModule" and "EditModule" into PolicyNames class. 2020-10-16 10:07:01 -05:00
955e7a3856 Factored out Contants.*** Role into RoleNames.***
Renamed 'AllUsers' to 'Everyone'
2020-10-16 06:22:52 -05:00
06c041dd4e Add Documentation attributes
part of https://github.com/oqtane/oqtane.framework/issues/570
2020-10-15 10:38:25 +02:00
4a90e6e64f Use ServiceActivator instead of IHttpContextAccessor 2020-10-15 06:07:11 +03:00
81475fd835 Merge pull request #796 from hishamco/localizable-labels
Support label localization
2020-10-13 07:52:24 -04:00
edc65e66c9 Use AddHttpContextAccessor() 2020-10-12 18:26:04 +03:00
4b11bdc4be Support label localization 2020-10-12 18:15:08 +03:00
67067a884b Merge pull request #791 from hishamco/localization-configuration
Simplify localization settings configurations
2020-10-11 20:34:56 -04:00
86bb6d1ea8 Simplify localization settings configurations 2020-10-10 22:19:21 +03:00
7b1a2fb887 Merge pull request #789 from hishamco/debug
Add blazor error details on DEV environment
2020-10-09 14:28:34 -04:00
b3db92ee95 Add blazor error details on DEV environment 2020-10-09 19:17:42 +03:00
aad10ab1c4 Changes for .NET 5 2020-10-08 11:20:43 -04:00
3ab9510e2a Update README.md 2020-10-08 09:59:05 -04:00
fde43b6c39 Merge pull request #783 from mikecasas/master
Refactor to eliminate repetitive code.
2020-10-07 09:24:43 -04:00
7b3dfc49b2 Refactor to eliminate repetitive code. 2020-10-06 08:11:00 -04:00
0a9edd8916 Merge pull request #781 from sbwalker/master
Fixed build warnings related to ModuleMessage component changes
2020-10-05 09:12:52 -04:00
0c0916c6ab Fixed build warnings related to ModuleMessage component changes 2020-10-05 09:11:47 -04:00
ece8f3a57e Merge pull request #56 from oqtane/master
sync
2020-10-04 10:57:24 -04:00
3d7630d3d4 Update README.md 2020-10-04 10:43:09 -04:00
8b6d0d3c7f Merge pull request #779 from hishamco/supported-cultures
Skip missed satellite assemblies forlders
2020-10-04 10:29:44 -04:00
ce37d2f2d2 Skip missed satellite assemblies forlders 2020-10-03 23:26:44 +03:00
40524300bf Merge pull request #777 from hishamco/remove-warning
Avoid Building ServiceProvider in ConfigureServices
2020-10-03 15:52:04 -04:00
5ae9daf5f2 Merge pull request #778 from sbwalker/master
added DefaultAction property to IModule (#765)
2020-10-03 15:51:30 -04:00
6a7be12758 added DefaultAction property to IModule (#765) 2020-10-03 15:50:15 -04:00
bcb6c81e43 Avoid Building ServiceProvider in ConfigureServices 2020-10-03 22:41:48 +03:00
26c40fb367 Merge pull request #55 from oqtane/master
sync
2020-10-03 15:15:16 -04:00
e934a28c39 Merge pull request #770 from PoisnFang/flexible-index-page-in-custom-modules
Allows page to find Custom Index page in Module from Actions Property
2020-10-03 15:14:18 -04:00
c2ca55627e comment where index page is specifed if no action 2020-10-03 12:12:23 -07:00
4a6ffacf56 Merge pull request #54 from oqtane/master
sync
2020-10-03 14:27:02 -04:00
bc72e28d11 Merge pull request #767 from PoisnFang/fix-pane-div-default-class
add default class 'container' for div in pane to avoid content squishing
2020-10-03 14:19:16 -04:00
2a402497cf only add div on admin border 2020-10-01 16:02:14 -07:00
49985dcf9e Merge pull request #53 from oqtane/master
sync
2020-10-01 10:08:22 -04:00
666721bf1a Merge pull request #762 from hishamco/localization-support
Localization support
2020-10-01 10:06:01 -04:00
5f56bc288b Merge pull request #750 from hishamco/alert-component
Use ModuleMessage component everywhere
2020-10-01 10:05:43 -04:00
6e41cd850e allows page to find Custom Index page in Module from Actions Property 2020-09-30 22:53:41 -07:00
f70fed66ae add default class 'container' for div in pane to avoid content squishing 2020-09-30 16:22:46 -07:00
2e2d46996a Refactoring 2020-09-30 00:07:00 +03:00
f83c1b1741 Use invariant culture by default 2020-09-29 22:12:03 +03:00
2924e7849f Read supported cultures from appsettings.json 2020-09-29 21:23:22 +03:00
437170671f Support server-sider localization 2020-09-29 20:20:38 +03:00
b52dd571ee Fix loading bug 2020-09-29 19:18:56 +03:00
ad9146ead1 Fix stallite assemblies folder path 2020-09-29 19:14:48 +03:00
52d1d5841e Avoid looking for en-US culture resources 2020-09-29 18:50:06 +03:00
468327d597 Set localization RootNamespace to make it works 2020-09-29 18:29:18 +03:00
7f28c5f2ff Add localization service to Oqtane.Client 2020-09-29 18:28:30 +03:00
accf947afd LoadClientAssemblies adds satellite assemblies 2020-09-29 18:28:02 +03:00
ec73c958c9 AddOqtaneParts -> AddOqtane 2020-09-29 18:03:24 +03:00
396d584615 Dowanlod satellite assemblies to the browser 2020-09-29 18:01:57 +03:00
edecfa10cd Load satellite assemblies on startup 2020-09-29 17:31:54 +03:00
0796ce54a9 Add localization settings 2020-09-29 17:30:56 +03:00
0044f031aa Set component params instead of SetModuleMessage 2020-09-23 11:29:05 +03:00
913ad53302 Use ModuleMessage everywhere 2020-09-20 15:43:01 +03:00
ad5f5fbc24 Replace Alert with ModuleMessage component 2020-09-20 15:10:48 +03:00
14746f47da Merge pull request #746 from mikecasas/patch-2
Update Constants.cs
2020-09-17 18:53:15 -04:00
0d76070663 Use alert component in FileManager 2020-09-17 15:42:03 +03:00
c01cd3b46c Add dismissible option 2020-09-17 15:41:34 +03:00
3613ce62eb Add Alert component 2020-09-17 15:09:20 +03:00
61839d8e46 Merge pull request #748 from sbwalker/master
fixed Theme install/uninstall issue, fixed Layout inheritance issue, fixed File server performance issue, cleaned up remaining hardcoded permission strings
2020-09-16 15:25:33 -04:00
8196112a59 fixed Theme install/uninstall issue, fixed Layout inheritance issue, fixed File server performance issue, cleaned up remaining hardcoded permission strings 2020-09-16 15:24:07 -04:00
465cbe3c96 Update Constants.cs
Hopefully, nothing wrong with uploading a csv file.
2020-09-16 14:18:22 -04:00
38f2fa5733 Merge pull request #735 from sbwalker/master
prepare for 1.0.4 release
2020-09-09 12:02:11 -04:00
7f15a5f464 prepare for 1.0.4 release 2020-09-09 12:01:16 -04:00
0c5d992d18 Merge pull request #52 from oqtane/master
sync
2020-09-09 11:49:45 -04:00
57dd983c1f Update README.md 2020-09-03 15:59:29 -04:00
d89927ca96 Update README.md 2020-09-02 15:20:17 -04:00
63744d9ec2 Update README.md 2020-09-02 15:17:56 -04:00
c67526b5b0 Update README.md 2020-09-01 16:44:33 -04:00
1cb6bf2a6b Merge pull request #724 from sbwalker/master
removed background color
2020-09-01 16:28:16 -04:00
ac9969c1b6 removed background color 2020-09-01 16:27:42 -04:00
510cd23d5e Update README.md 2020-09-01 16:24:27 -04:00
c94ccbff69 Merge pull request #723 from sbwalker/master
created architecture diagram
2020-09-01 16:23:21 -04:00
93d0cc5e1a created architecture diagram 2020-09-01 16:22:40 -04:00
075ea0aafd Merge pull request #51 from oqtane/master
sync
2020-08-31 10:04:27 -04:00
e75fe19103 Merge pull request #720 from sbwalker/master
add support for SVG and ICO files
2020-08-31 10:01:15 -04:00
e76f1b9663 use Label component in Module Creator templates 2020-08-31 10:00:30 -04:00
cb1c725ec1 add support for SVG and ICO files 2020-08-31 09:48:51 -04:00
98cd361fc0 Merge pull request #716 from sbwalker/master
enhanced Module Creator to allow developer to specify framework reference version so that modules can target any version including the local development environment
2020-08-29 11:30:53 -04:00
d0c8399dd9 enhanced Module Creator to allow developer to specify framework reference version so that modules can target any version including the local development environment 2020-08-29 11:30:16 -04:00
4effa8ec66 Merge pull request #715 from sbwalker/master
improved module/theme installation by saving the list of files which are in the Nuget package and using that list to remove them during uninstall
2020-08-29 10:56:26 -04:00
4065d87a74 improved module/theme installation by saving the list of files which are in the Nuget package and using that list to remove them during uninstall 2020-08-29 10:55:40 -04:00
eb9acc770c Merge pull request #714 from sbwalker/master
added support for dynamic inclusion of global resources in _host.cshtml ( ie. global stylesheets and scripts such as those required by UI component suites )
2020-08-28 11:25:38 -04:00
a8cd84e798 added support for dynamic inclusion of global resources in _host.cshtml ( ie. global stylesheets and scripts such as those required by UI component suites ) 2020-08-28 11:24:43 -04:00
74e5b83026 Merge pull request #711 from sbwalker/master
wired up JavaScript support in Module Creator templates
2020-08-27 17:17:32 -04:00
4aa0b83807 wired up JavaScript support in Module Creator templates 2020-08-27 17:16:54 -04:00
fd592e8d9f Merge pull request #707 from sbwalker/master
script file name should reflect next framework version
2020-08-26 15:25:54 -04:00
bb21eba39f script file name should reflect next major version 2020-08-26 15:24:44 -04:00
b09cb9d655 Merge pull request #50 from oqtane/master
sync
2020-08-26 15:16:28 -04:00
bbbe48b976 Merge pull request #700 from nohorse/patch-1
Create Tenant.01.00.02.02.sql
2020-08-26 15:08:35 -04:00
a036ee19a4 Merge pull request #698 from hishamco/logo
Fix logo
2020-08-26 15:02:43 -04:00
5b45c79357 Merge pull request #705 from sbwalker/master
Ensure folder does not contain files during deletion and remove directory during deletion, fix validation issue in add page which would allow a user to create a page without selecting a layout, modify action dialog to use its own CSS class name so it can be styled independently from the Admin Modal, rollback "container" CSS class assigment on panes
2020-08-26 15:01:09 -04:00
760fc3b8d4 Ensure folder does not contain files during deletion and remove directory during deletion, fix validation issue in add page which would allow a user to create a page without selecting a layout, modify action dialog to use its own CSS class name so it can be styled independently from the Admin Modal, rollback "container" CSS class assigment on panes 2020-08-26 15:00:07 -04:00
fc50a45ecd Create Tenant.01.00.02.02.sql
Proposed fixed for #699
2020-08-22 14:55:43 -07:00
e3fe8c5914 Fix logo 2020-08-22 04:19:11 +03:00
2624b9c105 Merge pull request #691 from mikecasas/plural-fix
Delete module pluralization in the location display.
2020-08-19 05:09:54 -07:00
2f9f823330 Delete module pluralization in the location display. 2020-08-18 17:02:40 -04:00
6cc144d733 Merge pull request #49 from oqtane/master
sync
2020-08-18 13:38:24 -07:00
df404c12a4 Merge pull request #686 from mikecasas/master
Add project reference for dotnet publish to work without errors.
2020-08-18 13:37:36 -07:00
faec53b3c5 Merge pull request #688 from mikecasas/patch-1
Rename MenuHorizontal.Razor to MenuHorizontal.razor
2020-08-18 13:37:23 -07:00
e1ec58b297 Rename MenuHorizontal.Razor to MenuHorizontal.razor 2020-08-18 09:34:26 -04:00
69abce7ce4 Merge pull request #19 from oqtane/master
sync master
2020-08-18 09:14:20 -04:00
38738e0844 Add project reference for dotnet publish to work without errors. 2020-08-16 22:35:09 -04:00
abe0a1a806 Merge pull request #685 from sbwalker/master
resolved #604 - added support for renaming files and moving to a different folder. Also added support for renaming folders and moving to a different parent folder.
2020-08-16 16:03:10 -07:00
809946685a resolved #604 - added support for renaming files and moving to a different folder. Also added support for renaming folders and moving to a different parent folder. 2020-08-16 19:00:49 -04:00
20c8f1528d Merge pull request #683 from sbwalker/master
resolve #526 remove pluralization from module creation templates
2020-08-14 09:44:44 -07:00
282579fcf2 resolve #526 remove pluralization from module creation templates 2020-08-14 12:44:37 -04:00
c8e3fa88e7 Merge pull request #679 from sbwalker/master
Fix #676 - fix creation of new profile fields, add support for required and private profile fields, integrate field level help for consistency
2020-08-13 07:06:28 -07:00
aec5882de1 Fix #676 - fix creation of new profile fields, add support for required and private profile fields, integrate field level help for consistency 2020-08-13 10:06:15 -04:00
bc231b18cf Update README.md 2020-08-07 14:56:58 -04:00
6bcb769fe5 Merge pull request #675 from sbwalker/master
prepare for 1.0.3 release
2020-08-07 10:40:00 -07:00
90110a653c prepare for 1.0.3 release 2020-08-07 13:39:19 -04:00
3c561cc413 Merge pull request #48 from oqtane/master
sync
2020-08-07 10:25:15 -07:00
73f9622ba2 Merge pull request #674 from sbwalker/master
prepare for 1.0.3 release
2020-08-07 10:24:31 -07:00
cf198ff781 prepare for 1.0.3 release 2020-08-07 13:23:58 -04:00
648fc56495 Merge pull request #673 from sbwalker/master
fixed very large file upload
2020-08-07 08:46:57 -07:00
ea6dc6b983 fixed very large file upload 2020-08-07 11:46:11 -04:00
c0e8d09ce1 Merge pull request #672 from sbwalker/master
allow user to reinstall current version
2020-08-06 13:48:07 -07:00
a471784cf3 allow user to reinstall current version 2020-08-06 16:46:22 -04:00
1d2a4bf484 Merge pull request #671 from sbwalker/master
include logging during module and theme installation
2020-08-06 13:37:55 -07:00
3fa620f3bc include logging during module and theme installation 2020-08-06 16:37:27 -04:00
35f186b532 Merge pull request #670 from sbwalker/master
fix regression bug caused by #649 related to installing nupkg packages
2020-08-06 13:10:51 -07:00
5cf35fd70a fix regression bug caused by #649 related to installing nupkg packages 2020-08-06 16:10:19 -04:00
1eef08eaeb Merge pull request #669 from sbwalker/master
modifications for System Update
2020-08-06 10:30:40 -07:00
1750f28a9f modifications for System Update 2020-08-06 13:30:06 -04:00
41edbc5e22 Merge pull request #668 from sbwalker/master
modifications for System Update feature
2020-08-04 10:07:35 -07:00
04257f75e7 modifications for System Update feature 2020-08-04 13:06:54 -04:00
5fb602f733 Merge pull request #667 from sbwalker/master
Improvements to System Update
2020-08-04 05:48:19 -07:00
94f0bdcce9 Improvements to System Update 2020-08-04 08:47:39 -04:00
0ba24f9a3a Update README.md 2020-08-04 08:42:28 -04:00
aac7d6b97a Merge pull request #664 from sbwalker/master
create 1.0.2 packages
2020-07-23 15:32:17 -04:00
bcc33af52b create 1.0.2 packages 2020-07-23 15:31:28 -04:00
24fd42636a Merge pull request #663 from sbwalker/master
preparing for 1.0.2 release
2020-07-23 15:08:27 -04:00
8d539d058c preparing for 1.0.2 release 2020-07-23 15:07:18 -04:00
abda377f6f Merge pull request #662 from sbwalker/master
fix regression bug caused by #648  - the entries within a nupkg (zip) package use the '/' separator - the fix in #648 was causing a wwwroot\wwwroot\... folder to be created on Windows
2020-07-23 14:59:25 -04:00
51bf822392 fix regression bug caused by #648 - the entries within a nupkg (zip) package use the '/' separator - the fix in #648 was causing a wwwroot\wwwroot\... folder to be created on Windows 2020-07-23 14:58:33 -04:00
d3f135a9c7 Merge pull request #47 from oqtane/master
sync
2020-07-23 14:41:29 -04:00
07ba99cc41 Merge pull request #661 from sbwalker/master
increase wait time for browser redirects during app restarts
2020-07-23 14:40:46 -04:00
336550c571 increase wait time for browser redirects during app restarts 2020-07-23 14:39:53 -04:00
679cc04178 Merge pull request #660 from sbwalker/master
optimize NotificationJob so that it only processes the sites for each tenant once.
2020-07-23 14:15:24 -04:00
75fe4e7c89 optimize NotificationJob so that it only processes the sites for each tenant once. 2020-07-23 14:14:29 -04:00
410f8c74e5 Merge pull request #649 from JoergH66/feature/LoadDependDlls_InstallManager
Fixes 2 external module installation problems
2020-07-23 11:39:35 -04:00
05f67d6a2a Merge pull request #659 from sbwalker/master
fixed scheduler so that it does not set NextExecution until after the job has finished executing
2020-07-23 11:39:19 -04:00
3a6cde0e24 fixed scheduler so that it does not set NextExecution until after the job has finished executing 2020-07-23 11:38:20 -04:00
fe1de2b243 Merge pull request #658 from sbwalker/master
Allow scheduled jobs to set next execution date, fix issue in site settings where logo field was not being populated, fixed compositing issue where deleted modules were being rendered.
2020-07-22 16:10:23 -04:00
62a6b5f28a Allow scheduled jobs to set next execution date, fix issue in site settings where logo field was not being populated, fixed compositing issue where deleted modules were being rendered. 2020-07-22 16:09:39 -04:00
d648fa0f02 Merge pull request #657 from ADefWebserver/master
Make a Deploy to Azure Button #168
2020-07-21 11:07:36 -04:00
e706e8cf1f Update README.md
Points the button to the Oqtane repository
2020-07-18 09:55:27 -07:00
9eb8a7e65c Update azuredeploy.json 2020-07-18 09:40:40 -07:00
11c610edf0 Update .deployment 2020-07-18 09:18:02 -07:00
a65cdbd7ad Rename azure.deployment to .deployment 2020-07-18 08:12:18 -07:00
97c56ba142 Create azure.deployment 2020-07-18 08:02:03 -07:00
9fe72a1c98 Update azuredeploy.json 2020-07-17 18:45:57 -07:00
7b40725534 Update README.md 2020-07-17 18:15:08 -07:00
5e1671afe3 Create azuredeploy.json 2020-07-17 18:10:22 -07:00
50d74cbcee Merge pull request #656 from sbwalker/master
modifications to ActionLink and ActionDialog controls, added logic to prevent IHostedService from blocking startup, made controller route prefix consistent in module template
2020-07-17 15:11:32 -04:00
bc73e5e3d0 modifications to ActionLink and ActionDialog controls, added logic to prevent IHostedService from blocking startup, made controller route prefix consistent in module template 2020-07-17 15:09:05 -04:00
b02bdee8cb Merge pull request #46 from oqtane/master
sync
2020-07-17 10:10:23 -04:00
9db4985b14 Merge pull request #655 from alexhendel/fix-path-handling
Fix directory separator for path operations
2020-07-16 10:27:34 -04:00
6f281c256b Merge pull request #654 from mikecasas/upstream-local
Should be moduleid as the entity id is added in the CreateAuthPolicyU…
2020-07-16 10:27:19 -04:00
807252c9e5 Fix directory separator for path operations 2020-07-15 16:09:19 +02:00
23e7f66188 Delete module id as it is getting added in CreateAuthPolicyUrl method. 2020-07-14 11:47:59 -04:00
57c500f4bc Merge remote-tracking branch 'github-mike/upstream-local' into upstream-local 2020-07-14 11:42:55 -04:00
fe302aa9e4 Should be moduleid as the entity id is added in the CreateAuthPolicyUrl method. 2020-07-12 18:59:20 -04:00
f14f927df7 Update README.md 2020-07-09 10:19:20 -04:00
c3f74a5217 Update README.md 2020-07-09 10:18:06 -04:00
457d1bb563 Update README.md 2020-07-09 10:17:34 -04:00
25918056cb Update README.md 2020-07-09 10:14:53 -04:00
86517dd793 Update README.md 2020-07-09 10:13:07 -04:00
00ce083a2c Update README.md 2020-07-09 10:11:28 -04:00
bce262cd8e Merge pull request #652 from sbwalker/master
remove line feeds from content during import
2020-07-09 08:46:50 -04:00
b5db62ef6a remove line feeds from content during import 2020-07-09 08:45:23 -04:00
3703d87d50 Merge pull request #651 from sbwalker/master
Html encode job log messages, add new IModule property to allow modules to specify Runtime support, provide feedback during module content import, remove default EditMode option at the Page level (should be implemented at Module level) - resolves issue where Admin modules could not be deleted, include link to Event Log in AddModuleMessage for Error message type, fixed fallback support for themes in siterouter, integrated auth policy into site templates for Module Creator
2020-07-08 19:57:13 -04:00
f515def414 Html encode job log messages, add new IModule property to allow modules to specify Runtime support, provide feedback during module content import, remove default EditMode option at the Page level (should be implemented at Module level) - resolves issue where Admin modules could not be deleted, include link to Event Log in AddModuleMessage for Error message type, fixed fallback support for themes in siterouter, integrated auth policy into site templates for Module Creator 2020-07-08 19:56:02 -04:00
4bdf20822f check whether the file is in use, dependent runtime-dlls will distribute 2020-07-08 14:06:41 +02:00
49f4e64cb4 Merge pull request #45 from oqtane/master
sync
2020-07-07 08:44:51 -04:00
e615263706 Merge pull request #647 from chlupac/Notifications
Notification changes UNDO
2020-07-07 08:43:52 -04:00
2a7e256116 Merge pull request #648 from alexhendel/fix-linux-theme-installation
Fix directory separator in InstallationManager
2020-07-07 08:43:37 -04:00
a083405b48 Fix directory seperator in InstallationManager 2020-07-07 09:56:24 +02:00
3ea280c82a Motification changes UNDO 2020-07-06 15:27:35 +02:00
192433f02d Merge pull request #44 from oqtane/master
sync
2020-07-05 11:32:20 -04:00
56a2e9dcea Merge pull request #644 from chlupac/ActionIcons
Icons in module actions menu
2020-07-03 15:16:47 -04:00
921cced1c8 Merge pull request #643 from chlupac/Notifications
Notification job optimalization
2020-07-03 15:16:14 -04:00
b17f679f38 Notification job optimalization 2020-07-03 10:19:12 +02:00
8e43fcab21 Icons in module actions menu 2020-07-03 10:15:45 +02:00
73c5092e46 Merge pull request #43 from oqtane/master
sync
2020-07-02 08:56:55 -04:00
56537e4785 Merge pull request #642 from svreic/bugfix/page-path-validation
Page path validation
2020-07-02 08:13:39 -04:00
3bd7d7196d Merge pull request #640 from PoisnFang/routing
Url parameter helper enhancements
2020-07-02 08:11:14 -04:00
7b5a192b82 Added double page path validation 2020-07-02 09:47:42 +02:00
d4be058d07 Can get parameters without template
clear urlParameters dictionary if template fails. Removed UrlParametersTemplate property and UrlParamerters auto dictionary
2020-07-01 15:15:39 -07:00
6c20fea46a Merge pull request #641 from chlupac/NotifyRepo
Notification Repository Breaking change fix
2020-07-01 14:22:26 -04:00
2e7cfefb2e Notification Repository Breaking change fix 2020-07-01 14:23:55 +02:00
038894cf64 Enhancement to url parameters helper in modulebase 2020-07-01 01:35:06 -07:00
954e30d89f Save url parameter action segments 2020-06-30 16:01:16 -07:00
93d9c4534d Merge pull request #42 from oqtane/master
sync
2020-06-30 16:56:08 -04:00
468ca8c6a9 Merge pull request #639 from PoisnFang/routing
Hot fix for homepage routing
2020-06-30 16:54:54 -04:00
e7a4c08dea Now also properly routes in admin modules 2020-06-30 13:51:48 -07:00
69d639ee42 Hot fix for homepage routing 2020-06-30 13:42:35 -07:00
a780569a6f Merge pull request #41 from oqtane/master
sync
2020-06-30 16:16:11 -04:00
568c283efd Merge pull request #638 from PoisnFang/routing
Module Router Enhancement
2020-06-30 16:15:27 -04:00
fccdd07a08 Replaced token identifiers for { } 2020-06-30 12:59:19 -07:00
5e816ea912 Removed anchor property and hash is only set if there is anchor 2020-06-30 12:49:56 -07:00
cb2d529689 added in GetUrlParameters route to Module Index action 2020-06-30 04:16:08 -07:00
c5037e7084 Url parameters working on any page, plus queries and anchors 2020-06-30 03:41:35 -07:00
fdc39d57fb Module Router Enhancement
Allows for PageVariables through the URL
2020-06-27 11:49:24 -07:00
4960e2c668 Update README.md 2020-06-26 08:45:13 -04:00
66cc3a1392 Merge pull request #637 from sbwalker/master
improvements for custom authorization policy usage
2020-06-25 10:24:54 -04:00
6e7c8e7b05 improvements for custom authorization policy usage 2020-06-25 10:23:27 -04:00
727b943fa3 Merge pull request #636 from sbwalker/master
added ModuleControlBase
2020-06-25 09:32:56 -04:00
a4a0334ec0 added ModuleControlBase 2020-06-25 09:31:21 -04:00
233 changed files with 3721 additions and 1738 deletions

2
.deployment Normal file
View File

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

53
.editorconfig Normal file
View File

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

View File

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

View File

@ -8,7 +8,7 @@
protected override async Task OnInitializedAsync()
{
Module module = await ModuleService.GetModuleAsync(ModuleState.ModuleId);
if (UserSecurity.IsAuthorized(PageState.User, Constants.HostRole))
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
{
string message = "A Problem Was Encountered Loading Module " + module.ModuleDefinitionName;
AddModuleMessage(message, MessageType.Error);

View File

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

View File

@ -1,6 +1,7 @@
@namespace Oqtane.Modules.Admin.Files
@inherits ModuleBase
@inject IFolderService FolderService
@inject IFileService FileService
@inject NavigationManager NavigationManager
@if (_folders != null)
@ -45,7 +46,7 @@
<NavLink class="btn btn-secondary" href="@NavigateUrl()">Cancel</NavLink>
@if (!_isSystem && PageState.QueryString.ContainsKey("id"))
{
<button type="button" class="btn btn-danger" @onclick="DeleteFolder">Delete</button>
<ActionDialog Header="Delete Folder" Message="@("Are You Sure You Wish To Delete This Folder?")" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteFolder())" />
}
<br />
<br />
@ -123,7 +124,7 @@
AddModuleMessage("Folder Name Not Valid.", MessageType.Warning);
return;
}
try
{
Folder folder;
@ -174,7 +175,7 @@
catch (Exception ex)
{
await logger.LogError(ex, "Error Saving Folder {FolderId} {Error}", _folderId, ex.Message);
AddModuleMessage("Error Saving Module", MessageType.Error);
AddModuleMessage("Error Saving Folder", MessageType.Error);
}
}
@ -182,9 +183,33 @@
{
try
{
await FolderService.DeleteFolderAsync(_folderId);
await logger.LogInformation("Folder Deleted {Folder}", _folderId);
AddModuleMessage("Folder Deleted", MessageType.Success);
bool isparent = false;
foreach (Folder folder in _folders)
{
if (folder.ParentId == _folderId)
{
isparent = true;
break;
}
}
if (!isparent)
{
var files = await FileService.GetFilesAsync(_folderId);
if (files.Count == 0)
{
await FolderService.DeleteFolderAsync(_folderId);
await logger.LogInformation("Folder Deleted {Folder}", _folderId);
NavigationManager.NavigateTo(NavigateUrl());
}
else
{
AddModuleMessage("Folder Has Files And Cannot Be Deleted", MessageType.Warning);
}
}
else
{
AddModuleMessage("Folder Has Subfolders And Cannot Be Deleted", MessageType.Warning);
}
}
catch (Exception ex)
{

View File

@ -28,6 +28,7 @@
</table>
<Pager Items="@_files">
<Header>
<th style="width: 1px;">&nbsp;</th>
<th style="width: 1px;">&nbsp;</th>
<th>Name</th>
<th>Modified</th>
@ -35,6 +36,7 @@
<th>Size</th>
</Header>
<Row>
<td><ActionLink Action="Details" Text="Edit" Parameters="@($"id=" + context.FileId.ToString())" /></td>
<td><ActionDialog Header="Delete File" Message="@("Are You Sure You Wish To Delete " + context.Name + "?")" Action="Delete" Security="SecurityAccessLevel.Admin" Class="btn btn-danger" OnClick="@(async () => await DeleteFile(context))" /></td>
<td><a href="@(ContentUrl(context.FileId))" target="_new">@context.Name</a></td>
<td>@context.ModifiedOn</td>

View File

@ -63,10 +63,18 @@
</tr>
<tr>
<td>
<Label For="retention-log" HelpText="What items do you want in the retention log">Retention Log (Items): </Label>
<Label For="retention" HelpText="Number of log entries to retain for this job">Retention Log (Items): </Label>
</td>
<td>
<input id="retention-log" class="form-control" @bind="@_retentionHistory" />
<input id="retention" class="form-control" @bind="@_retentionHistory" />
</td>
</tr>
<tr>
<td>
<Label For="next" HelpText="Next execution for this job.">Next Execution: </Label>
</td>
<td>
<input id="next" class="form-control" @bind="@_nextExecution" />
</td>
</tr>
</table>
@ -83,6 +91,7 @@
private string _startDate = string.Empty;
private string _endDate = string.Empty;
private string _retentionHistory = string.Empty;
private string _nextExecution = string.Empty;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
@ -102,6 +111,7 @@
_startDate = (job.StartDate != null) ? job.StartDate.ToString() : string.Empty;
_endDate = (job.EndDate != null) ? job.EndDate.ToString() : string.Empty;
_retentionHistory = job.RetentionHistory.ToString();
_nextExecution = job.NextExecution.ToString();
}
}
catch (Exception ex)
@ -140,6 +150,15 @@
job.EndDate = DateTime.Parse(_endDate);
}
if (_nextExecution == string.Empty)
{
job.NextExecution = null;
}
else
{
job.NextExecution = DateTime.Parse(_nextExecution);
}
job.RetentionHistory = int.Parse(_retentionHistory);
try

View File

@ -22,7 +22,7 @@ else
<td>@context.FinishDate</td>
</Row>
<Detail>
<td colspan="4">@context.Notes</td>
<td colspan="4">@((MarkupString)context.Notes)</td>
</Detail>
</Pager>
}

View File

@ -1,4 +1,4 @@
@namespace Oqtane.Modules.Admin.Login
@namespace Oqtane.Modules.Admin.Login
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IUserService UserService
@ -16,14 +16,14 @@
<ModuleMessage Message="You Are Already Logged In" Type="MessageType.Info" />
</Authorized>
<NotAuthorized>
<div class="container">
<div class="container Oqtane-Modules-Admin-Login">
<div class="form-group">
<label for="Username" class="control-label">Username: </label>
<input type="text" name="Username" class="form-control" placeholder="Username" @bind="@_username" id="Username" />
<input type="text" name="Username" class="form-control username" placeholder="Username" @bind="@_username" id="Username" />
</div>
<div class="form-group">
<label for="Password" class="control-label">Password: </label>
<input type="password" name="Password" class="form-control" placeholder="Password" @bind="@_password" id="Password" />
<input type="password" name="Password" class="form-control password" placeholder="Password" @bind="@_password" id="Password" />
</div>
<div class="form-group">
<div class="form-check form-check-inline">
@ -49,18 +49,23 @@
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Anonymous;
public override List<Resource> Resources => new List<Resource>()
{
new Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css" }
};
protected override async Task OnInitializedAsync()
{
if (PageState.QueryString.ContainsKey("returnurl"))
{
_returnUrl = PageState.QueryString["returnurl"];
}
if (PageState.QueryString.ContainsKey("name"))
{
_username = PageState.QueryString["name"];
}
if (PageState.QueryString.ContainsKey("token"))
{
var user = new User();
@ -90,7 +95,7 @@
user.Username = _username;
user.Password = _password;
user = await UserService.LoginUserAsync(user, false, false);
if (user.IsAuthenticated)
{
await logger.LogInformation("Login Successful For Username {Username}", _username);
@ -154,7 +159,7 @@
{
_message = "Please Enter The Username Related To Your Account And Then Select The Forgot Password Option Again";
}
StateHasChanged();
}
}

View File

@ -1,59 +1,76 @@
@namespace Oqtane.Modules.Admin.ModuleCreator
@namespace Oqtane.Modules.Admin.ModuleCreator
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IModuleDefinitionService ModuleDefinitionService
@inject IModuleService ModuleService
@inject ISystemService SystemService
<table class="table table-borderless">
<tr>
<td>
<Label For="owner" HelpText="Enter the name of the organization who is developing this module. It should not contain spaces or punctuation.">Owner Name: </Label>
</td>
<td>
<input id="owner" class="form-control" @bind="@_owner" />
</td>
</tr>
<tr>
<td>
<Label For="module" HelpText="Enter a name for this module. It should be in singular form (ie. Car) and not contain spaces or punctuation.">Module Name: </Label>
</td>
<td>
<input id="module" class="form-control" @bind="@_module" />
</td>
</tr>
<tr>
<td>
<Label For="description" HelpText="Enter s short description for the module">Description: </Label>
</td>
<td>
<textarea id="description" class="form-control" @bind="@_description" rows="3"></textarea>
</td>
</tr>
<tr>
<td>
<Label For="template" HelpText="Select a module template. Internal modules are created inside of the Oqtane solution. External modules are created outside of the Oqtane solution.">Template: </Label>
</td>
<td>
<select id="template" class="form-control" @onchange="(e => TemplateChanged(e))">
<option value="-">&lt;Select Template&gt;</option>
<option value="internal">Internal</option>
<option value="external">External</option>
</select>
</td>
</tr>
@if (!string.IsNullOrEmpty(_location))
{
<table class="table table-borderless">
<tr>
<td>
<Label For="location" HelpText="Location where the module will be created">Location: </Label>
<Label For="owner" HelpText="Enter the name of the organization who is developing this module. It should not contain spaces or punctuation.">Owner Name: </Label>
</td>
<td>
<input id="module" class="form-control" @bind="@_location" readonly />
<input id="owner" class="form-control" @bind="@_owner" />
</td>
</tr>
}
</table>
<tr>
<td>
<Label For="module" HelpText="Enter a name for this module. It should not contain spaces or punctuation.">Module Name: </Label>
</td>
<td>
<input id="module" class="form-control" @bind="@_module" />
</td>
</tr>
<tr>
<td>
<Label For="description" HelpText="Enter s short description for the module">Description: </Label>
</td>
<td>
<textarea id="description" class="form-control" @bind="@_description" rows="3"></textarea>
</td>
</tr>
<tr>
<td>
<Label For="template" HelpText="Select a module template. Internal modules are created inside of the Oqtane solution. External modules are created outside of the Oqtane solution.">Template: </Label>
</td>
<td>
<select id="template" class="form-control" @onchange="(e => TemplateChanged(e))">
<option value="-">&lt;Select Template&gt;</option>
<option value="internal">Internal</option>
<option value="external">External</option>
</select>
</td>
</tr>
<tr>
<td>
<Label For="reference" HelpText="Select a framework reference version">Framework Reference: </Label>
</td>
<td>
<select id="reference" class="form-control" @bind="@_reference">
@foreach (string version in Constants.ReleaseVersions.Split(','))
{
if (Version.Parse(version).CompareTo(Version.Parse("2.0.0")) >= 0)
{
<option value="@(version)">@(version)</option>
}
}
<option value="local">Local Version</option>
</select>
</td>
</tr>
@if (!string.IsNullOrEmpty(_location))
{
<tr>
<td>
<Label For="location" HelpText="Location where the module will be created">Location: </Label>
</td>
<td>
<input id="module" class="form-control" @bind="@_location" readonly />
</td>
</tr>
}
</table>
<button type="button" class="btn btn-success" @onclick="CreateModule">Create Module</button>
@ -62,13 +79,14 @@
private string _module = string.Empty;
private string _description = string.Empty;
private string _template = "-";
public string _reference = Constants.Version;
private string _location = string.Empty;
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
protected override void OnInitialized()
{
AddModuleMessage("Please Note That Once You Select The Create Module Button The Application Must Restart In Order To Complete The Process. If You Create An External Module You Will Need To Compile The Source Code In Order To Make It Functional.", MessageType.Info);
AddModuleMessage("Please Note That Once You Select The Create Module Button The Application Must Restart In Order To Complete The Process. If You Create An External Module You Will Need To Compile The Source Code And Then Relaunch Your Site In Order To Make It Functional.", MessageType.Info);
}
private async Task CreateModule()
@ -77,7 +95,11 @@
{
if (!string.IsNullOrEmpty(_owner) && !string.IsNullOrEmpty(_module) && _template != "-")
{
var moduleDefinition = new ModuleDefinition { Owner = _owner.Replace(" ", ""), Name = _module.Replace(" ", ""), Description = _description, Template = _template };
ShowProgressIndicator();
var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 5);
var moduleDefinition = new ModuleDefinition { Owner = _owner.Replace(" ", ""), Name = _module.Replace(" ", ""), Description = _description, Template = _template, Version = _reference };
await ModuleDefinitionService.CreateModuleDefinitionAsync(moduleDefinition, ModuleState.ModuleId);
}
else
@ -105,11 +127,11 @@
string[] path = systeminfo["serverpath"].Split('\\');
if (_template == "internal")
{
_location = string.Join("\\", path, 0, path.Length - 1) + "\\Oqtane.Client\\Modules\\" + _owner + "." + _module + "s";
_location = string.Join("\\", path, 0, path.Length - 1) + "\\Oqtane.Client\\Modules\\" + _owner + "." + _module;
}
else
{
_location = string.Join("\\", path, 0, path.Length - 2) + "\\" + _owner + "." + _module + "s";
_location = string.Join("\\", path, 0, path.Length - 2) + "\\" + _owner + "." + _module;
}
}
}

View File

@ -79,7 +79,7 @@
{
ShowProgressIndicator();
var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 3);
await interop.RedirectBrowser(NavigateUrl(), 10);
await ModuleDefinitionService.InstallModuleDefinitionsAsync();
}
catch (Exception ex)

View File

@ -75,12 +75,20 @@
</tr>
<tr>
<td>
<Label For="license" HelpText="The license of the module">License: </Label>
<Label For="license" HelpText="The module license terms">License: </Label>
</td>
<td>
<textarea id="license" class="form-control" @bind="@_license" rows="5" disabled></textarea>
</td>
</tr>
<tr>
<td>
<Label For="runtimes" HelpText="The Blazor runtimes which this module supports">Runtimes: </Label>
</td>
<td>
<input id="runtimes" class="form-control" @bind="@_runtimes" disabled />
</td>
</tr>
</table>
</Section>
</TabPanel>
@ -110,6 +118,7 @@
private string _url = "";
private string _contact = "";
private string _license = "";
private string _runtimes = "";
private string _permissions;
private string _createdby;
private DateTime _createdon;
@ -139,6 +148,7 @@
_url = moduleDefinition.Url;
_contact = moduleDefinition.Contact;
_license = moduleDefinition.License;
_runtimes = moduleDefinition.Runtimes;
_permissions = moduleDefinition.Permissions;
_createdby = moduleDefinition.CreatedBy;
_createdon = moduleDefinition.CreatedOn;

View File

@ -86,7 +86,7 @@ else
await logger.LogInformation("Module Downloaded {ModuleDefinitionName} {Version}", moduledefinitionname, version);
ShowProgressIndicator();
var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 3);
await interop.RedirectBrowser(NavigateUrl(), 10);
await ModuleDefinitionService.InstallModuleDefinitionsAsync();
}
catch (Exception ex)
@ -102,7 +102,7 @@ else
{
ShowProgressIndicator();
var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 3);
await interop.RedirectBrowser(NavigateUrl(), 10);
await ModuleDefinitionService.DeleteModuleDefinitionAsync(moduleDefinition.ModuleDefinitionId, moduleDefinition.SiteId);
}
catch (Exception ex)

View File

@ -31,9 +31,15 @@
{
try
{
await ModuleService.ImportModuleAsync(ModuleState.ModuleId, _content);
StateHasChanged();
NavigationManager.NavigateTo(NavigateUrl());
bool success = await ModuleService.ImportModuleAsync(ModuleState.ModuleId, _content);
if (success)
{
AddModuleMessage("Content Imported Successfully", MessageType.Success);
}
else
{
AddModuleMessage("A Problem Was Encountered Importing Content. Please Ensure The Content Is Formatted Correctly For The Module.", MessageType.Warning);
}
}
catch (Exception ex)
{

View File

@ -162,17 +162,6 @@
<input id="Icon" class="form-control" @bind="@_icon" />
</td>
</tr>
<tr>
<td>
<Label For="Default-Mode" HelpText="Select the default administration mode you want for this page">Default Mode? </Label>
</td>
<td>
<select id="Default-Mode" class="form-control" @bind="@_mode">
<option value="view">View Mode</option>
<option value="edit">Edit Mode</option>
</select>
</td>
</tr>
<tr>
<td>
<Label For="Personalizable" HelpText="Select whether you would like users to be able to personalize this page with their own content">Personalizable? </Label>
@ -217,7 +206,6 @@
private string _isnavigation = "True";
private string _url;
private string _ispersonalizable = "False";
private string _mode = "view";
private string _themetype = "-";
private string _layouttype = "-";
private string _containertype = "-";
@ -311,7 +299,7 @@
Page page = null;
try
{
if (_name != string.Empty && !string.IsNullOrEmpty(_themetype) && (_layouts.Count == 0 || !string.IsNullOrEmpty(_layouttype)))
if (_name != string.Empty && !string.IsNullOrEmpty(_themetype) && (_layouts.Count == 0 || _layouttype != "-"))
{
page = new Page();
page.SiteId = PageState.Page.SiteId;
@ -346,6 +334,12 @@
}
}
if (!PagePathIsUnique(page.Path, page.SiteId, _pageList))
{
AddModuleMessage($"A page with path {_path} already exists for the selected parent page. The page path needs to be unique for the selected parent.", MessageType.Warning);
return;
}
Page child;
switch (_insert)
{
@ -367,7 +361,6 @@
page.IsNavigation = (_isnavigation == null ? true : Boolean.Parse(_isnavigation));
page.Url = _url;
page.EditMode = (_mode == "edit" ? true : false);
page.ThemeType = (_themetype != "-") ? _themetype : string.Empty;
if (!string.IsNullOrEmpty(page.ThemeType) && page.ThemeType == PageState.Site.DefaultThemeType)
{
@ -396,7 +389,7 @@
}
else
{
AddModuleMessage("You Must Provide Page Name And Theme", MessageType.Warning);
AddModuleMessage("You Must Provide Page Name And Theme/Layout", MessageType.Warning);
}
}
@ -407,4 +400,8 @@
}
}
private static bool PagePathIsUnique(string pagePath, int siteId, List<Page> existingPages)
{
return !existingPages.Any(page => page.SiteId == siteId && page.Path == pagePath);
}
}

View File

@ -173,17 +173,6 @@
<input id="Icon" class="form-control" @bind="@_icon" />
</td>
</tr>
<tr>
<td>
<Label For="Default-Mode" HelpText="Select the default administration mode you want for this page">Default Mode? </Label>
</td>
<td>
<select id="Default-Mode" class="form-control" @bind="@_mode">
<option value="view">View Mode</option>
<option value="edit">Edit Mode</option>
</select>
</td>
</tr>
<tr>
<td>
<Label For="Personalizable" HelpText="Select whether you would like users to be able to personalize this page with their own content">Personalizable? </Label>
@ -235,7 +224,6 @@
private string _isnavigation;
private string _url;
private string _ispersonalizable;
private string _mode;
private string _themetype = "-";
private string _layouttype = "-";
private string _containertype = "-";
@ -290,7 +278,6 @@
_isnavigation = page.IsNavigation.ToString();
_url = page.Url;
_ispersonalizable = page.IsPersonalizable.ToString();
_mode = (page.EditMode) ? "edit" : "view";
_themetype = page.ThemeType;
if (_themetype == PageState.Site.DefaultThemeType)
{
@ -333,7 +320,7 @@
_children = new List<Page>();
if (_parentid == "-1")
{
foreach(Page p in PageState.Pages.Where(item => item.ParentId == null))
foreach (Page p in PageState.Pages.Where(item => item.ParentId == null))
{
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.Permissions))
{
@ -433,6 +420,13 @@
page.Path = parent.Path + "/" + Utilities.GetFriendlyUrl(_path);
}
}
if (!PagePathIsUnique(page.Path, page.SiteId, page.PageId, _pageList))
{
AddModuleMessage($"A page with path {_path} already exists for the selected parent page. The page path needs to be unique for the selected parent.", MessageType.Warning);
return;
}
if (_insert != "=")
{
Page child;
@ -456,7 +450,6 @@
}
page.IsNavigation = (_isnavigation == null || Boolean.Parse(_isnavigation));
page.Url = _url;
page.EditMode = (_mode == "edit");
page.ThemeType = (_themetype != "-") ? _themetype : string.Empty;
if (!string.IsNullOrEmpty(page.ThemeType) && page.ThemeType == PageState.Site.DefaultThemeType)
{
@ -512,4 +505,9 @@
AddModuleMessage("Error Saving Page", MessageType.Error);
}
}
private static bool PagePathIsUnique(string pagePath, int siteId, int pageId, List<Page> existingPages)
{
return !existingPages.Any(page => page.SiteId == siteId && page.Path == pagePath && page.PageId != pageId);
}
}

View File

@ -1,4 +1,4 @@
@namespace Oqtane.Modules.Admin.Profiles
@namespace Oqtane.Modules.Admin.Profiles
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IProfileService ProfileService
@ -6,7 +6,7 @@
<table class="table table-borderless">
<tr>
<td>
<Label For="name" HelpText="The name of this field">Name: </Label>
<Label For="name" HelpText="The name of this profile item">Name: </Label>
</td>
<td>
<input id="name" class="form-control" @bind="@_name" />
@ -14,7 +14,7 @@
</tr>
<tr>
<td>
<Label For="title" HelpText="The title of the field">Title: </Label>
<Label For="title" HelpText="The title of the profile item to display to the user">Title: </Label>
</td>
<td>
<input id="title" class="form-control" @bind="@_title" />
@ -22,7 +22,7 @@
</tr>
<tr>
<td>
<Label For="description" HelpText="What the profile field is">Description: </Label>
<Label For="description" HelpText="The help text displayed to the user for this profile item">Description: </Label>
</td>
<td>
<textarea id="description" class="form-control" @bind="@_description" rows="5"></textarea>
@ -30,7 +30,7 @@
</tr>
<tr>
<td>
<Label For="category" HelpText="What larger category does this field belong to">Category: </Label>
<Label For="category" HelpText="The category of this profile item (for grouping)">Category: </Label>
</td>
<td>
<input id="category" class="form-control" @bind="@_category" />
@ -38,7 +38,7 @@
</tr>
<tr>
<td>
<Label For="order" HelpText="What place is this field in a larger category list">Order: </Label>
<Label For="order" HelpText="The index order of where this profile item should be displayed">Order: </Label>
</td>
<td>
<input id="order" class="form-control" @bind="@_vieworder" />
@ -46,7 +46,7 @@
</tr>
<tr>
<td>
<Label For="length" HelpText="What is the max amount of characters should this field accept">Length: </Label>
<Label For="length" HelpText="The max number of characters this profile item should accept (enter zero for unlimited)">Length: </Label>
</td>
<td>
<input id="length" class="form-control" @bind="@_maxlength" />
@ -54,7 +54,7 @@
</tr>
<tr>
<td>
<Label For="defaultVal" HelpText="What value do you want this field to start with">Default Value: </Label>
<Label For="defaultVal" HelpText="The default value for this profile item">Default Value: </Label>
</td>
<td>
<input id="defaultVal" class="form-control" @bind="@_defaultvalue" />
@ -62,7 +62,15 @@
</tr>
<tr>
<td>
<Label For="required" HelpText="Is a user required to input something into this field?">Required? </Label>
<Label For="options" HelpText="A comma delimited list of options the user can select from">Options: </Label>
</td>
<td>
<input id="options" class="form-control" @bind="@_options" />
</td>
</tr>
<tr>
<td>
<Label For="required" HelpText="Should a user be required to provide a value for this profile item?">Required? </Label>
</td>
<td>
<select id="required" class="form-control" @bind="@_isrequired">
@ -73,7 +81,7 @@
</tr>
<tr>
<td>
<Label For="private" HelpText="Is this field private?">Private? </Label>
<Label For="private" HelpText="Should this profile item be visible to all users?">Private? </Label>
</td>
<td>
<select id="private" class="form-control" @bind="@_isprivate">
@ -95,6 +103,7 @@
private string _vieworder = "0";
private string _maxlength = "0";
private string _defaultvalue = string.Empty;
private string _options = string.Empty;
private string _isrequired = "False";
private string _isprivate = "False";
@ -119,6 +128,7 @@
_vieworder = profile.ViewOrder.ToString();
_maxlength = profile.MaxLength.ToString();
_defaultvalue = profile.DefaultValue;
_options = profile.Options;
_isrequired = profile.IsRequired.ToString();
_isprivate = profile.IsPrivate.ToString();
}
@ -145,6 +155,7 @@
profile = new Profile();
}
profile.SiteId = PageState.Site.SiteId;
profile.Name = _name;
profile.Title = _title;
profile.Description = _description;
@ -152,6 +163,7 @@
profile.ViewOrder = int.Parse(_vieworder);
profile.MaxLength = int.Parse(_maxlength);
profile.DefaultValue = _defaultvalue;
profile.Options = _options;
profile.IsRequired = (_isrequired == null ? false : Boolean.Parse(_isrequired));
profile.IsPrivate = (_isprivate == null ? false : Boolean.Parse(_isprivate));
if (_profileid != -1)

View File

@ -88,7 +88,7 @@ else
Role role = await RoleService.GetRoleAsync(roleid);
name = role.Name;
users = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId);
users = users.Where(item => item.Role.Name == Constants.RegisteredRole).ToList();
users = users.Where(item => item.Role.Name == RoleNames.Registered).ToList();
await GetUserRoles();
}
catch (Exception ex)

View File

@ -7,7 +7,7 @@
@inject IThemeService ThemeService
@inject ISettingService SettingService
@if (_themes != null)
@if (_initialized)
{
<table class="table table-borderless">
<tr>
@ -211,6 +211,7 @@
}
@code {
private bool _initialized = false;
private List<Theme> _themeList;
private List<ThemeControl> _themes = new List<ThemeControl>();
private List<ThemeControl> _layouts = new List<ThemeControl>();
@ -318,6 +319,8 @@
_deletedby = site.DeletedBy;
_deletedon = site.DeletedOn;
_isdeleted = site.IsDeleted.ToString();
_initialized = true;
}
}
catch (Exception ex)
@ -445,7 +448,7 @@
}
else
{
AddModuleMessage("You Must Provide A Site Name, Alias, And Default Theme/Container", MessageType.Warning);
AddModuleMessage("You Must Provide A Site Name, Alias, And Default Theme/Layout/Container", MessageType.Warning);
}
}
catch (Exception ex)

View File

@ -216,7 +216,7 @@ else
private string _username = string.Empty;
private string _password = string.Empty;
private bool _integratedsecurity = true;
private string _hostusername = Constants.HostUser;
private string _hostusername = UserNames.Host;
private string _hostpassword = string.Empty;
private string _name = string.Empty;
@ -311,7 +311,7 @@ else
// validate host credentials
var user = new User();
user.SiteId = PageState.Site.SiteId;
user.Username = Constants.HostUser;
user.Username = UserNames.Host;
user.Password = _hostpassword;
user = await UserService.LoginUserAsync(user, false, false);
if (user.IsAuthenticated)
@ -402,7 +402,7 @@ else
}
else
{
AddModuleMessage("You Must Provide A Tenant, Site Name, Alias, Default Theme/Container, And Site Template", MessageType.Warning);
AddModuleMessage("You Must Provide A Tenant, Site Name, Alias, Default Theme/Layout/Container, And Site Template", MessageType.Warning);
}
}
}

View File

@ -6,7 +6,7 @@
@inject IAliasService AliasService
@inject IThemeService ThemeService
@if (_themes != null)
@if (_initialized)
{
<table class="table table-borderless">
<tr>
@ -106,6 +106,7 @@
}
@code {
private bool _initialized = false;
private List<Theme> _themeList;
private List<ThemeControl> _themes = new List<ThemeControl>();
private List<ThemeControl> _layouts = new List<ThemeControl>();
@ -163,6 +164,8 @@
_deletedby = site.DeletedBy;
_deletedon = site.DeletedOn;
_isdeleted = site.IsDeleted.ToString();
_initialized = true;
}
}
catch (Exception ex)

View File

@ -9,7 +9,7 @@
<Label For="name" HelpText="The name of the tenant">Name: </Label>
</td>
<td>
@if (name == Constants.MasterTenant)
@if (name == TenantNames.Master)
{
<input id="name" class="form-control" @bind="@name" readonly />
}

View File

@ -17,7 +17,7 @@ else
</Header>
<Row>
<td><ActionLink Action="Edit" Parameters="@($"id=" + context.TenantId.ToString())" /></td>
<td><ActionDialog Header="Delete Tenant" Message="@("Are You Sure You Wish To Delete The " + context.Name + " Tenant?")" Action="Delete" Security="SecurityAccessLevel.Host" Class="btn btn-danger" OnClick="@(async () => await DeleteTenant(context))" Disabled="@(context.Name == Constants.MasterTenant)" /></td>
<td><ActionDialog Header="Delete Tenant" Message="@("Are You Sure You Wish To Delete The " + context.Name + " Tenant?")" Action="Delete" Security="SecurityAccessLevel.Host" Class="btn btn-danger" OnClick="@(async () => await DeleteTenant(context))" Disabled="@(context.Name == TenantNames.Master)" /></td>
<td>@context.Name</td>
</Row>
</Pager>

View File

@ -79,7 +79,7 @@
{
ShowProgressIndicator();
var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 3);
await interop.RedirectBrowser(NavigateUrl(), 10);
await ThemeService.InstallThemesAsync();
}
catch (Exception ex)

View File

@ -87,7 +87,7 @@ else
await logger.LogInformation("Theme Downloaded {ThemeName} {Version}", themename, version);
ShowProgressIndicator();
var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 3);
await interop.RedirectBrowser(NavigateUrl(), 10);
await ThemeService.InstallThemesAsync();
}
catch (Exception ex)
@ -103,7 +103,7 @@ else
{
ShowProgressIndicator();
var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 3);
await interop.RedirectBrowser(NavigateUrl(), 10);
await ThemeService.DeleteThemeAsync(Theme.ThemeName);
}
catch (Exception ex)

View File

@ -12,7 +12,7 @@
@if (_upgradeavailable)
{
<ModuleMessage Type="MessageType.Info" Message="Select The Upgrade Button To Install a New Framework Version"></ModuleMessage>
@("Framework") @_package.Version <button type="button" class="btn btn-success" @onclick=@(async () => await Download(Constants.PackageId, Constants.Version))>Upgrade</button>
<button type="button" class="btn btn-success" @onclick=@(async () => await Download(Constants.PackageId, @_package.Version))>Upgrade To @_package.Version</button>
}
else
{
@ -26,7 +26,7 @@
<Label HelpText="Upload a framework package and select Install to complete the installation">Framework: </Label>
</td>
<td>
<FileManager Filter="nupkg" Folder="Framework" />
<FileManager Filter="nupkg" ShowFiles="false" Folder="Framework" />
</td>
</tr>
</table>
@ -71,7 +71,7 @@
{
ShowProgressIndicator();
var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 3);
await interop.RedirectBrowser(NavigateUrl(), 10);
await InstallationService.Upgrade();
}
catch (Exception ex)
@ -88,7 +88,7 @@
await PackageService.DownloadPackageAsync(packageid, version, "Framework");
ShowProgressIndicator();
var interop = new Interop(JSRuntime);
await interop.RedirectBrowser(NavigateUrl(), 3);
await interop.RedirectBrowser(NavigateUrl(), 10);
await InstallationService.Upgrade();
}
catch (Exception ex)

View File

@ -1,4 +1,4 @@
@namespace Oqtane.Modules.Admin.UserProfile
@namespace Oqtane.Modules.Admin.UserProfile
@inherits ModuleBase
@inject NavigationManager NavigationManager
@inject IUserService UserService
@ -75,10 +75,12 @@ else
<TabPanel Name="Profile">
@if (profiles != null && settings != null)
{
<table class="table table-borderless">
@foreach (Profile profile in profiles)
<table class="table table-borderless">
@foreach (Profile profile in profiles)
{
var p = profile;
if (!p.IsPrivate || UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
{
var p = profile;
if (p.Category != category)
{
<tr>
@ -90,14 +92,41 @@ else
}
<tr>
<td>
<label for="@p.Name" class="control-label">@p.Title: </label>
<Label For="@p.Name" HelpText="@p.Description">@p.Title</Label>
</td>
<td>
<input class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" placeholder="@p.Description" @onchange="@(e => ProfileChanged(e, p.Name))" />
@if (!string.IsNullOrEmpty(p.Options))
{
<select id="@p.Name" class="form-control" @onchange="@(e => ProfileChanged(e, p.Name))">
@foreach (var option in p.Options.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
@if(GetProfileValue(p.Name, "") == option || (GetProfileValue(p.Name, "") == "" && p.DefaultValue == option))
{
<option value="@option" selected>@option</option>
}
else
{
<option value="@option">@option</option>
}
}
</select>
}
else
{
@if (p.IsRequired)
{
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" required @onchange="@(e => ProfileChanged(e, p.Name))" />
}
else
{
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" />
}
}
</td>
</tr>
}
</table>
}
</table>
<button type="button" class="btn btn-primary" @onclick="Save">Save</button>
<button type="button" class="btn btn-secondary" @onclick="Cancel">Cancel</button>
}
@ -241,7 +270,7 @@ else
{
try
{
if (username != string.Empty && email != string.Empty)
if (username != string.Empty && email != string.Empty && ValidateProfiles())
{
if (password == confirm)
{
@ -261,6 +290,7 @@ else
await UserService.UpdateUserAsync(user);
await SettingService.UpdateUserSettingsAsync(settings, PageState.User.UserId);
await logger.LogInformation("User Profile Saved");
AddModuleMessage("User Profile Updated Successfully", MessageType.Success);
}
else
{
@ -269,7 +299,7 @@ else
}
else
{
AddModuleMessage("You Must Provide A Username and Email Address", MessageType.Warning);
AddModuleMessage("You Must Provide A Username and Email Address As Well As All Required Profile Information", MessageType.Warning);
}
}
catch (Exception ex)
@ -279,6 +309,26 @@ else
}
}
private bool ValidateProfiles()
{
bool valid = true;
foreach (Profile profile in profiles)
{
if (string.IsNullOrEmpty(SettingService.GetSetting(settings, profile.Name, string.Empty)) && !string.IsNullOrEmpty(profile.DefaultValue))
{
settings = SettingService.SetSetting(settings, profile.Name, profile.DefaultValue);
}
if (!profile.IsPrivate || UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
{
if (profile.IsRequired && string.IsNullOrEmpty(SettingService.GetSetting(settings, profile.Name, string.Empty)))
{
valid = false;
}
}
}
return valid;
}
private void Cancel()
{
NavigationManager.NavigateTo(NavigateUrl(string.Empty));

View File

@ -71,10 +71,17 @@
}
<tr>
<td>
<label for="@p.Name" class="control-label">@p.Title: </label>
<Label For="@p.Name" HelpText="@p.Description">@p.Title</Label>
</td>
<td>
<input class="form-control" maxlength="@p.MaxLength" placeholder="@p.Description" @onchange="@(e => ProfileChanged(e, p.Name))" />
@if (p.IsRequired)
{
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" required @onchange="@(e => ProfileChanged(e, p.Name))" />
}
else
{
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" />
}
</td>
</tr>
}
@ -112,11 +119,14 @@
}
}
private string GetProfileValue(string SettingName, string DefaultValue)
=> SettingService.GetSetting(settings, SettingName, DefaultValue);
private async Task SaveUser()
{
try
{
if (username != string.Empty && password != string.Empty && confirm != string.Empty && email != string.Empty)
if (username != string.Empty && password != string.Empty && confirm != string.Empty && email != string.Empty && ValidateProfiles())
{
if (password == confirm)
{
@ -149,7 +159,7 @@
}
else
{
AddModuleMessage("You Must Provide A Username, Password, and Email Address", MessageType.Warning);
AddModuleMessage("You Must Provide A Username, Password, Email Address And All Required Profile Information", MessageType.Warning);
}
}
catch (Exception ex)
@ -159,6 +169,23 @@
}
}
private bool ValidateProfiles()
{
bool valid = true;
foreach (Profile profile in profiles)
{
if (string.IsNullOrEmpty(SettingService.GetSetting(settings, profile.Name, string.Empty)) && !string.IsNullOrEmpty(profile.DefaultValue))
{
settings = SettingService.SetSetting(settings, profile.Name, profile.DefaultValue);
}
if (profile.IsRequired && string.IsNullOrEmpty(SettingService.GetSetting(settings, profile.Name, string.Empty)))
{
valid = false;
}
}
return valid;
}
private void ProfileChanged(ChangeEventArgs e, string SettingName)
{
var value = (string)e.Value;

View File

@ -98,10 +98,17 @@ else
}
<tr>
<td>
<label for="@p.Name" class="control-label">@p.Title: </label>
<Label For="@p.Name" HelpText="@p.Description">@p.Title</Label>
</td>
<td>
<input class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" placeholder="@p.Description" @onchange="@(e => ProfileChanged(e, p.Name))" />
@if (p.IsRequired)
{
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" required @onchange="@(e => ProfileChanged(e, p.Name))" />
}
else
{
<input id="@p.Name" class="form-control" maxlength="@p.MaxLength" value="@GetProfileValue(p.Name, p.DefaultValue)" @onchange="@(e => ProfileChanged(e, p.Name))" />
}
</td>
</tr>
}
@ -180,7 +187,7 @@ else
{
try
{
if (username != string.Empty && email != string.Empty)
if (username != string.Empty && email != string.Empty && ValidateProfiles())
{
if (password == confirm)
{
@ -213,7 +220,7 @@ else
}
else
{
AddModuleMessage("You Must Provide A Username, Password, and Email Address", MessageType.Warning);
AddModuleMessage("You Must Provide A Username, Password, Email Address, And All Required Profile Information", MessageType.Warning);
}
}
catch (Exception ex)
@ -223,6 +230,23 @@ else
}
}
private bool ValidateProfiles()
{
bool valid = true;
foreach (Profile profile in profiles)
{
if (string.IsNullOrEmpty(SettingService.GetSetting(settings, profile.Name, string.Empty)) && !string.IsNullOrEmpty(profile.DefaultValue))
{
settings = SettingService.SetSetting(settings, profile.Name, profile.DefaultValue);
}
if (profile.IsRequired && string.IsNullOrEmpty(SettingService.GetSetting(settings, profile.Name, string.Empty)))
{
valid = false;
}
}
return valid;
}
private void ProfileChanged(ChangeEventArgs e, string SettingName)
{
var value = (string)e.Value;

View File

@ -58,10 +58,10 @@ else
{
if (string.IsNullOrEmpty(_search))
{
return allroles.Where(item => item.Role.Name == Constants.RegisteredRole).ToList();
return allroles.Where(item => item.Role.Name == RoleNames.Registered).ToList();
}
return allroles
.Where(item => item.Role.Name == Constants.RegisteredRole &&
.Where(item => item.Role.Name == RoleNames.Registered &&
(
item.User.Username.Contains(search, StringComparison.OrdinalIgnoreCase) ||
item.User.Email.Contains(search, StringComparison.OrdinalIgnoreCase) ||

View File

@ -63,7 +63,7 @@ else
<Row>
<td>@context.Role.Name</td>
<td>
@if (context.Role.Name != Constants.RegisteredRole)
@if (context.Role.Name != RoleNames.Registered)
{
<button type="button" class="btn btn-danger" @onclick=@(async () => await DeleteUserRole(context.UserRoleId))>Delete</button>
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,56 @@
using System;
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Localization;
using Oqtane.Shared;
namespace Oqtane.Modules.Controls
{
public class LocalizableComponent : ModuleControlBase
{
private IStringLocalizer _localizer;
[Parameter]
public string ResourceKey { get; set; }
protected bool IsLocalizable { get; private set; }
protected string Localize(string name)
{
var key = $"{ResourceKey}.{name}";
// TODO: we should have a ShowMissingResourceKeys option which developers/translators can enable to find missing translations which would display the key rather than the name
if (!IsLocalizable)
{
return name;
}
return _localizer?[key] ?? name;
}
protected override void OnParametersSet()
{
if (!String.IsNullOrEmpty(ResourceKey))
{
if (ModuleState?.ModuleType != null)
{
var moduleType = Type.GetType(ModuleState.ModuleType);
if (moduleType != null)
{
using (var scope = ServiceActivator.GetScope())
{
var localizerFactory = scope.ServiceProvider.GetService<IStringLocalizerFactory>();
_localizer = localizerFactory.Create(moduleType);
}
}
}
IsLocalizable = true;
}
else
{
IsLocalizable = false;
}
}
}
}

View File

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

View File

@ -1,41 +1,39 @@
@namespace Oqtane.Modules.Controls
@inherits ModuleBase
@attribute [OqtaneIgnore]
@typeparam TableItem
@inherits ModuleControlBase
@typeparam TableItem
<p>
@if(Format == "Table")
@if (Format == "Table")
{
<table class="@Class">
<thead>
<tr>@Header</tr>
</thead>
<tbody>
<table class="@Class">
<thead>
<tr>@Header</tr>
</thead>
<tbody>
@foreach (var item in ItemList)
{
<tr>@Row(item)</tr>
@if (Detail != null)
{
<tr>@Detail(item)</tr>
}
}
</tbody>
</table>
}
@if (Format == "Grid")
{
<div class="@Class">
<div class="row">@Header</div>
@foreach (var item in ItemList)
{
<tr>@Row(item)</tr>
<div class="row">@Row(item)</div>
@if (Detail != null)
{
<tr>@Detail(item)</tr>
<div class="row">@Detail(item)</div>
}
}
</tbody>
</table>
}
@if(Format == "Grid")
{
<div class="@Class">
<div class="row">@Header</div>
@foreach (var item in ItemList)
{
<div class="row">@Row(item)</div>
@if (Detail != null)
{
<div class="row">@Detail(item)</div>
}
}
</div>
</div>
}
<div class="mx-auto text-center">
@if (_page > _maxPages)
@ -44,7 +42,7 @@
}
@if (_endPage > 1)
{
<button class="btn btn-secondary" @onclick=@(async () => NavigateToPage("previous"))><span class="oi oi-chevron-left" title="previous" aria-hidden="true"></span></button>
<button class="btn btn-secondary" @onclick=@(async () => NavigateToPage("previous"))><span class="oi oi-chevron-left" title="previous" aria-hidden="true"></span></button>
@for (int i = _startPage; i <= _endPage; i++)
{
var pager = i;
@ -105,7 +103,7 @@
{
Format = "Table";
}
if (string.IsNullOrEmpty(Class))
{
if (Format == "Table")
@ -117,7 +115,7 @@
Class = "container";
}
}
if (string.IsNullOrEmpty(PageSize))
{
_maxItems = 10;
@ -126,7 +124,7 @@
{
_maxItems = int.Parse(PageSize);
}
if (string.IsNullOrEmpty(DisplayPages))
{
_maxPages = 5;
@ -149,7 +147,7 @@
{
ItemList = Items.Skip((currentPage - 1) * _maxItems).Take(_maxItems);
_page = currentPage;
StateHasChanged();
}
@ -174,7 +172,7 @@
{
_endPage = _pages;
}
StateHasChanged();
}
else if (direction == "back")
@ -208,7 +206,7 @@
_page -= 1;
}
}
UpdateList(_page);
}
}

View File

@ -1,6 +1,5 @@
@namespace Oqtane.Modules.Controls
@inherits ModuleBase
@attribute [OqtaneIgnore]
@inherits ModuleControlBase
@inject IRoleService RoleService
@inject IUserService UserService
@ -39,7 +38,7 @@
<th scope="col">User</th>
@foreach (PermissionString permission in _permissions)
{
<th style="text-align: center; width: 1px;">@permission.PermissionName</th>
<th style="text-align: center; width: 1px;">@permission.PermissionName</th>
}
</tr>
</thead>
@ -48,7 +47,7 @@
{
string userid = "[" + user.UserId.ToString() + "]";
<tr>
<td >@user.DisplayName</td>
<td>@user.DisplayName</td>
@foreach (PermissionString permission in _permissions)
{
var p = permission;
@ -104,16 +103,16 @@
}
_roles = await RoleService.GetRolesAsync(ModuleState.SiteId);
_roles.Insert(0, new Role { Name = Constants.AllUsersRole });
_roles.Insert(0, new Role { Name = RoleNames.Everyone });
_permissions = new List<PermissionString>();
foreach (string permissionname in _permissionnames.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
// initialize with admin role
_permissions.Add(new PermissionString { PermissionName = permissionname, Permissions = Constants.AdminRole });
_permissions.Add(new PermissionString { PermissionName = permissionname, Permissions = RoleNames.Admin });
}
if (!string.IsNullOrEmpty(Permissions))
{
// populate permissions
@ -123,7 +122,7 @@
{
_permissions[_permissions.FindIndex(item => item.PermissionName == permissionstring.PermissionName)].Permissions = permissionstring.Permissions;
}
if (permissionstring.Permissions.Contains("["))
{
foreach (string user in permissionstring.Permissions.Split(new char[] { '[' }, StringSplitOptions.RemoveEmptyEntries))
@ -162,7 +161,7 @@
}
private bool GetPermissionDisabled(string roleName)
=> roleName == Constants.AdminRole
=> roleName == RoleNames.Admin
? true
: false;
@ -183,7 +182,7 @@
_message = "Username Does Not Exist";
}
}
_username = string.Empty;
}
@ -209,7 +208,7 @@
case null:
break; // permission not specified
}
_permissions[_permissions.FindIndex(item => item.PermissionName == permissionName)].Permissions = string.Join(";", ids.ToArray());
}
}
@ -227,8 +226,8 @@
{
permission = _permissions[i];
List<string> ids = permission.Permissions.Split(';').ToList();
ids.Remove("!" + Constants.AllUsersRole); // remove deny all users
ids.Remove("!" + Constants.RegisteredRole); // remove deny registered users
ids.Remove("!" + RoleNames.Everyone); // remove deny all users
ids.Remove("!" + RoleNames.Registered); // remove deny registered users
permission.Permissions = string.Join(";", ids.ToArray());
_permissions[i] = permission;
}

View File

@ -1,6 +1,5 @@
@namespace Oqtane.Modules.Controls
@inherits ModuleBase
@attribute [OqtaneIgnore]
@inherits ModuleControlBase
<div class="row" style="margin-bottom: 50px;">
<div class="col">
@ -9,7 +8,7 @@
@if (_filemanagervisible)
{
<FileManager @ref="_fileManager" Filter="@Constants.ImageFiles" />
@((MarkupString)_message)
<ModuleMessage Message="@_message" Type="MessageType.Warning"></ModuleMessage>
<br />
}
<div class="row justify-content-center" style="margin-bottom: 20px;">
@ -113,7 +112,7 @@
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill-blot-formatter.min.js" },
new Resource { ResourceType = ResourceType.Script, Bundle = "Quill", Url = "js/quill-interop.js" }
};
protected override void OnInitialized()
{
_content = Content; // raw HTML
@ -182,6 +181,7 @@
public async Task InsertImage()
{
_message = string.Empty;
if (_filemanagervisible)
{
var fileid = _fileManager.GetFileId();
@ -190,17 +190,15 @@
var interop = new RichTextEditorInterop(JSRuntime);
await interop.InsertImage(_editorElement, ContentUrl(fileid));
_filemanagervisible = false;
_message = string.Empty;
}
else
{
_message = "<br /><div class=\"alert alert-warning\" role=\"alert\">You Must Select An Image To Insert</div>";
_message = "You Must Select An Image To Insert";
}
}
else
{
_filemanagervisible = true;
_message = string.Empty;
}
StateHasChanged();
}

View File

@ -1,6 +1,5 @@
@namespace Oqtane.Modules.Controls
@inherits ModuleBase
@attribute [OqtaneIgnore]
@namespace Oqtane.Modules.Controls
@inherits LocalizableComponent
<div class="d-flex">
<div>
@ -42,4 +41,16 @@
_heading = (!string.IsNullOrEmpty(Heading)) ? Heading : Name;
_expanded = (!string.IsNullOrEmpty(Expanded)) ? Expanded : "false";
}
protected override void OnParametersSet()
{
base.OnParametersSet();
if (IsLocalizable)
{
_heading = !string.IsNullOrEmpty(Heading)
? Localize(nameof(Heading))
: Localize(nameof(Name));
}
}
}

View File

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

View File

@ -1,6 +1,5 @@
@namespace Oqtane.Modules.Controls
@inherits ModuleBase
@attribute [OqtaneIgnore]
@namespace Oqtane.Modules.Controls
@inherits ModuleControlBase
<CascadingValue Value="this">
<div class="container-fluid">
@ -8,20 +7,23 @@
<ul class="nav nav-tabs" role="tablist">
@foreach (TabPanel tabPanel in _tabPanels)
{
<li class="nav-item">
@if (tabPanel.Name == ActiveTab)
{
<a class="nav-link active" data-toggle="tab" href="#@tabPanel.Name" role="tab" @onclick:preventDefault="true">
@DisplayHeading(tabPanel.Name, tabPanel.Heading)
</a>
}
else
{
<a class="nav-link" data-toggle="tab" href="#@tabPanel.Name" role="tab" @onclick:preventDefault="true">
@DisplayHeading(tabPanel.Name, tabPanel.Heading)
</a>
}
</li>
@if (IsAuthorized(tabPanel))
{
<li class="nav-item">
@if (tabPanel.Name == ActiveTab)
{
<a class="nav-link active" data-toggle="tab" href="#@tabPanel.Name" role="tab" @onclick:preventDefault="true">
@tabPanel.DisplayHeading()
</a>
}
else
{
<a class="nav-link" data-toggle="tab" href="#@tabPanel.Name" role="tab" @onclick:preventDefault="true">
@tabPanel.DisplayHeading()
</a>
}
</li>
}
}
</ul>
<div class="tab-content">
@ -59,8 +61,30 @@
StateHasChanged();
}
private string DisplayHeading(string Name, string Heading)
private bool IsAuthorized(TabPanel tabPanel)
{
return (string.IsNullOrEmpty(Heading)) ? Name : Heading;
var authorized = false;
switch (tabPanel.Security)
{
case null: // security not specified - assume SecurityAccessLevel.Anonymous
authorized = true;
break;
case SecurityAccessLevel.Anonymous:
authorized = true;
break;
case SecurityAccessLevel.View:
authorized = UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, ModuleState.Permissions);
break;
case SecurityAccessLevel.Edit:
authorized = UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, ModuleState.Permissions);
break;
case SecurityAccessLevel.Admin:
authorized = UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin);
break;
case SecurityAccessLevel.Host:
authorized = UserSecurity.IsAuthorized(PageState.User, RoleNames.Host);
break;
}
return authorized;
}
}

View File

@ -1,4 +1,5 @@
@namespace Oqtane.Modules.Controls
@inherits ModuleControlBase
<img src="@_src" title="@_title" @onclick="SetValue" />
@ -38,7 +39,7 @@
_value = true;
break;
}
SetImage();
OnChange(_value);
}

View File

@ -7,7 +7,7 @@
@if (PageState.EditMode)
{
<br /><ActionLink Action="Edit" /><br /><br />
<br /><ActionLink Action="Edit" EditMode="true" /><br /><br />
}
@code {

View File

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

View File

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

View File

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

View File

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

View File

@ -1,12 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<OutputType>Exe</OutputType>
<LangVersion>7.3</LangVersion>
<RazorLangVersion>3.0</RazorLangVersion>
<Configurations>Debug;Release</Configurations>
<Version>1.0.1</Version>
<Version>2.0.0</Version>
<Product>Oqtane</Product>
<Authors>Shaun Walker</Authors>
<Company>.NET Foundation</Company>
@ -15,7 +14,7 @@
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
<RepositoryUrl>https://github.com/oqtane</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v1.0.1</PackageReleaseNotes>
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.0</PackageReleaseNotes>
<RootNamespace>Oqtane</RootNamespace>
<IsPackable>true</IsPackable>
</PropertyGroup>
@ -28,11 +27,11 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="3.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Build" Version="3.2.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="3.2.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="3.1.4" />
<PackageReference Include="System.Net.Http.Json" Version="3.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="5.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="5.0.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Localization" Version="5.0.0" />
<PackageReference Include="System.Net.Http.Json" Version="5.0.0" />
</ItemGroup>
<ItemGroup>

View File

@ -1,19 +1,19 @@
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Microsoft.Extensions.DependencyInjection;
using System.Threading.Tasks;
using Oqtane.Services;
using System.Reflection;
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Json;
using Oqtane.Modules;
using Oqtane.Shared;
using Oqtane.Providers;
using System.Reflection;
using System.Threading.Tasks;
using System.Runtime.Loader;
using Microsoft.AspNetCore.Components.Authorization;
using System.IO.Compression;
using System.IO;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Oqtane.Modules;
using Oqtane.Providers;
using Oqtane.Shared;
using Oqtane.Services;
namespace Oqtane.Client
{
@ -28,6 +28,9 @@ namespace Oqtane.Client
builder.Services.AddSingleton(httpClient);
builder.Services.AddOptions();
// Register localization services
builder.Services.AddLocalization(options => options.ResourcesPath = "Resources");
// register auth services
builder.Services.AddAuthorizationCore();
builder.Services.AddScoped<IdentityAuthenticationStateProvider>();
@ -62,32 +65,33 @@ namespace Oqtane.Client
await LoadClientAssemblies(httpClient);
// dynamically register module contexts and repository services
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (Assembly assembly in assemblies)
var assemblies = AppDomain.CurrentDomain.GetOqtaneAssemblies();
foreach (var assembly in assemblies)
{
var implementationTypes = assembly.GetTypes()
.Where(item => item.GetInterfaces().Contains(typeof(IService)));
foreach (Type implementationtype in implementationTypes)
// dynamically register module services
var implementationTypes = assembly.GetInterfaces<IService>();
foreach (var implementationType in implementationTypes)
{
Type servicetype = Type.GetType(implementationtype.AssemblyQualifiedName.Replace(implementationtype.Name, "I" + implementationtype.Name));
if (servicetype != null)
if (implementationType.AssemblyQualifiedName != null)
{
builder.Services.AddScoped(servicetype, implementationtype); // traditional service interface
}
else
{
builder.Services.AddScoped(implementationtype, implementationtype); // no interface defined for service
var serviceType = Type.GetType(implementationType.AssemblyQualifiedName.Replace(implementationType.Name, $"I{implementationType.Name}"));
builder.Services.AddScoped(serviceType ?? implementationType, implementationType);
}
}
assembly.GetInstances<IClientStartup>()
.ToList()
.ForEach(x => x.ConfigureServices(builder.Services));
// register client startup services
var startUps = assembly.GetInstances<IClientStartup>();
foreach (var startup in startUps)
{
startup.ConfigureServices(builder.Services);
}
}
await builder.Build().RunAsync();
var host = builder.Build();
ServiceActivator.Configure(host.Services);
await host.RunAsync();
}
private static async Task LoadClientAssemblies(HttpClient http)
@ -101,8 +105,8 @@ namespace Oqtane.Client
// asemblies and debug symbols are packaged in a zip file
using (ZipArchive archive = new ZipArchive(new MemoryStream(zip)))
{
Dictionary<string, byte[]> dlls = new Dictionary<string, byte[]>();
Dictionary<string, byte[]> pdbs = new Dictionary<string, byte[]>();
var dlls = new Dictionary<string, byte[]>();
var pdbs = new Dictionary<string, byte[]>();
foreach (ZipArchiveEntry entry in archive.Entries)
{
@ -129,11 +133,11 @@ namespace Oqtane.Client
{
if (pdbs.ContainsKey(item.Key))
{
Assembly.Load(item.Value, pdbs[item.Key]);
AssemblyLoadContext.Default.LoadFromStream(new MemoryStream(item.Value), new MemoryStream(pdbs[item.Key]));
}
else
{
Assembly.Load(item.Value);
AssemblyLoadContext.Default.LoadFromStream(new MemoryStream(item.Value));
}
}
}

View File

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

View File

@ -23,7 +23,7 @@
</div>
<div class="@BodyClass">
@if (UserSecurity.IsAuthorized(PageState.User, Constants.AdminRole))
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
{
<div class="row">
<div class="col">
@ -51,7 +51,7 @@
</div>
<br />
<div class="row">
@if (UserSecurity.GetPermissionStrings(PageState.Page.Permissions).FirstOrDefault(item => item.PermissionName == PermissionNames.View).Permissions.Split(';').Contains(Constants.AllUsersRole))
@if (UserSecurity.GetPermissionStrings(PageState.Page.Permissions).FirstOrDefault(item => item.PermissionName == PermissionNames.View).Permissions.Split(';').Contains(RoleNames.Everyone))
{
<div class="col">
<button type="button" class="btn btn-primary btn-block mx-auto" @onclick=@(async () => Publish("unpublish"))>Unpublish Page</button>
@ -127,7 +127,10 @@
{
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Utilize, moduledefinition.Permissions))
{
<option value="@moduledefinition.ModuleDefinitionName">@moduledefinition.Name</option>
if (moduledefinition.Runtimes == "" || moduledefinition.Runtimes.Contains(PageState.Runtime.ToString()))
{
<option value="@moduledefinition.ModuleDefinitionName">@moduledefinition.Name</option>
}
}
}
</select>
@ -196,26 +199,17 @@
@if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions) || (PageState.Page.IsPersonalizable && PageState.User != null))
{
@if (PageState.Page.EditMode)
if (PageState.EditMode)
{
<button type="button" class="btn @ButtonClass active" aria-pressed="true" autocomplete="off">
<button type="button" class="btn @ButtonClass active" data-toggle="button" aria-pressed="true" autocomplete="off" @onclick="(async () => await ToggleEditMode(PageState.EditMode))">
<span class="oi oi-pencil"></span>
</button>
}
else
{
if (PageState.EditMode)
{
<button type="button" class="btn @ButtonClass active" data-toggle="button" aria-pressed="true" autocomplete="off" @onclick="(async () => await ToggleEditMode(PageState.EditMode))">
<span class="oi oi-pencil"></span>
</button>
}
else
{
<button type="button" class="btn @ButtonClass" data-toggle="button" aria-pressed="false" autocomplete="off" @onclick="(async () => await ToggleEditMode(PageState.EditMode))">
<span class="oi oi-pencil"></span>
</button>
}
<button type="button" class="btn @ButtonClass" data-toggle="button" aria-pressed="false" autocomplete="off" @onclick="(async () => await ToggleEditMode(PageState.EditMode))">
<span class="oi oi-pencil"></span>
</button>
}
}
@ -516,8 +510,8 @@
if (permissionstring.PermissionName == PermissionNames.View)
{
List<string> ids = permissionstring.Permissions.Split(';').ToList();
if (!ids.Contains(Constants.AllUsersRole)) ids.Add(Constants.AllUsersRole);
if (!ids.Contains(Constants.RegisteredRole)) ids.Add(Constants.RegisteredRole);
if (!ids.Contains(RoleNames.Everyone)) ids.Add(RoleNames.Everyone);
if (!ids.Contains(RoleNames.Registered)) ids.Add(RoleNames.Registered);
permissionstring.Permissions = string.Join(";", ids.ToArray());
}
}
@ -537,12 +531,12 @@
switch (action)
{
case "publish":
if (!ids.Contains(Constants.AllUsersRole)) ids.Add(Constants.AllUsersRole);
if (!ids.Contains(Constants.RegisteredRole)) ids.Add(Constants.RegisteredRole);
if (!ids.Contains(RoleNames.Everyone)) ids.Add(RoleNames.Everyone);
if (!ids.Contains(RoleNames.Registered)) ids.Add(RoleNames.Registered);
break;
case "unpublish":
ids.Remove(Constants.AllUsersRole);
ids.Remove(Constants.RegisteredRole);
ids.Remove(RoleNames.Everyone);
ids.Remove(RoleNames.Registered);
break;
}
permissionstring.Permissions = string.Join(";", ids.ToArray());

View File

@ -2,7 +2,7 @@
@inherits ModuleActionsBase
@attribute [OqtaneIgnore]
@if (PageState.EditMode && !PageState.Page.EditMode && UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, ModuleState.Permissions))
@if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, ModuleState.Permissions))
{
<div class="app-moduleactions">
<a class="nav-link dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"></a>
@ -15,9 +15,20 @@
}
else
{
<a class="dropdown-item" @onclick="(async () => await ModuleAction(action))">@action.Name</a>
<a class="dropdown-item" @onclick="(async () => await ModuleAction(action))">
@if (string.IsNullOrEmpty(action.Icon))
{
@((MarkupString) "&nbsp;&nbsp;");
}
else
{
<span class="oi oi-@action.Icon" aria-hidden="true"></span>
}
&nbsp;
@action.Name
</a>
}
}
</div>
</div>
}
}

View File

@ -19,7 +19,7 @@ namespace Oqtane.Themes.Controls
[Inject] public IPageModuleService PageModuleService { get; set; }
[Inject] public IModuleService ModuleService { get; set; }
protected List<ActionViewModel> Actions;
public List<ActionViewModel> Actions;
protected override void OnParametersSet()
{
@ -31,51 +31,52 @@ namespace Oqtane.Themes.Controls
var actionList = new List<ActionViewModel>();
if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, ModuleState.Permissions))
{
actionList.Add(new ActionViewModel {Name = "Manage Settings", Action = async (u, m) => await Settings(u, m)});
if (UserSecurity.GetPermissionStrings(ModuleState.Permissions).FirstOrDefault(item => item.PermissionName == PermissionNames.View).Permissions.Split(';').Contains(Constants.AllUsersRole))
actionList.Add(new ActionViewModel {Icon = Icons.Cog, Name = "Manage Settings", Action = async (u, m) => await Settings(u, m)});
if (UserSecurity.GetPermissionStrings(ModuleState.Permissions).FirstOrDefault(item => item.PermissionName == PermissionNames.View).Permissions.Split(';').Contains(RoleNames.Everyone))
{
actionList.Add(new ActionViewModel { Name = "Unpublish Module", Action = async (s, m) => await Unpublish(s, m) });
actionList.Add(new ActionViewModel {Icon=Icons.CircleX, Name = "Unpublish Module", Action = async (s, m) => await Unpublish(s, m) });
}
else
{
actionList.Add(new ActionViewModel { Name = "Publish Module", Action = async (s, m) => await Publish(s, m) });
actionList.Add(new ActionViewModel {Icon=Icons.CircleCheck, Name = "Publish Module", Action = async (s, m) => await Publish(s, m) });
}
actionList.Add(new ActionViewModel { Name = "Delete Module", Action = async (u, m) => await DeleteModule(u, m) });
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 != "")
{
actionList.Add(new ActionViewModel { Name = "" });
actionList.Add(new ActionViewModel {Name = "Import Content", Action = async (u, m) => await EditUrlAsync(u, m.ModuleId, "Import")});
actionList.Add(new ActionViewModel {Name = "Export Content", Action = async (u, m) => await EditUrlAsync(u, m.ModuleId, "Export")});
actionList.Add(new ActionViewModel {Icon=Icons.CloudUpload, Name = "Import Content", Action = async (u, m) => await EditUrlAsync(u, m.ModuleId, "Import")});
actionList.Add(new ActionViewModel {Icon = Icons.CloudDownload, Name = "Export Content", Action = async (u, m) => await EditUrlAsync(u, m.ModuleId, "Export")});
}
actionList.Add(new ActionViewModel {Name = ""});
if (ModuleState.PaneModuleIndex > 0)
{
actionList.Add(new ActionViewModel {Name = "Move To Top", Action = async (s, m) => await MoveTop(s, m)});
actionList.Add(new ActionViewModel {Icon = Icons.DataTransferUpload ,Name = "Move To Top", Action = async (s, m) => await MoveTop(s, m)});
}
if (ModuleState.PaneModuleIndex > 0)
{
actionList.Add(new ActionViewModel {Name = "Move Up", Action = async (s, m) => await MoveUp(s, m)});
actionList.Add(new ActionViewModel {Icon = Icons.ArrowThickTop, Name = "Move Up", Action = async (s, m) => await MoveUp(s, m)});
}
if (ModuleState.PaneModuleIndex < (ModuleState.PaneModuleCount - 1))
{
actionList.Add(new ActionViewModel {Name = "Move Down", Action = async (s, m) => await MoveDown(s, m)});
actionList.Add(new ActionViewModel {Icon = Icons.ArrowThickBottom, Name = "Move Down", Action = async (s, m) => await MoveDown(s, m)});
}
if (ModuleState.PaneModuleIndex < (ModuleState.PaneModuleCount - 1))
{
actionList.Add(new ActionViewModel {Name = "Move To Bottom", Action = async (s, m) => await MoveBottom(s, m)});
actionList.Add(new ActionViewModel {Icon = Icons.DataTransferDownload, Name = "Move To Bottom", Action = async (s, m) => await MoveBottom(s, m)});
}
foreach (string pane in PageState.Page.Panes)
{
if (pane != ModuleState.Pane)
{
actionList.Add(new ActionViewModel {Name = "Move To " + pane + " Pane", Action = async (s, m) => await MoveToPane(s, pane, m)});
actionList.Add(new ActionViewModel {Icon = Icons.AccountLogin, Name = "Move To " + pane + " Pane", Action = async (s, m) => await MoveToPane(s, pane, m)});
}
}
}
@ -140,8 +141,8 @@ namespace Oqtane.Themes.Controls
if (permissionstring.PermissionName == PermissionNames.View)
{
List<string> ids = permissionstring.Permissions.Split(';').ToList();
if (!ids.Contains(Constants.AllUsersRole)) ids.Add(Constants.AllUsersRole);
if (!ids.Contains(Constants.RegisteredRole)) ids.Add(Constants.RegisteredRole);
if (!ids.Contains(RoleNames.Everyone)) ids.Add(RoleNames.Everyone);
if (!ids.Contains(RoleNames.Registered)) ids.Add(RoleNames.Registered);
permissionstring.Permissions = string.Join(";", ids.ToArray());
}
}
@ -158,8 +159,8 @@ namespace Oqtane.Themes.Controls
if (permissionstring.PermissionName == PermissionNames.View)
{
List<string> ids = permissionstring.Permissions.Split(';').ToList();
ids.Remove(Constants.AllUsersRole);
ids.Remove(Constants.RegisteredRole);
ids.Remove(RoleNames.Everyone);
ids.Remove(RoleNames.Registered);
permissionstring.Permissions = string.Join(";", ids.ToArray());
}
}
@ -202,8 +203,8 @@ namespace Oqtane.Themes.Controls
public class ActionViewModel
{
public string Icon { get; set; }
public string Name { set; get; }
public Func<string, PageModule, Task<string>> Action { set; get; }
}
}

View File

@ -1,4 +1,4 @@
@namespace Oqtane.Themes.Controls
@namespace Oqtane.Themes.Controls
@inherits ContainerBase
@attribute [OqtaneIgnore]
@ -17,6 +17,13 @@
{
title = PageState.Action;
}
else
{
if (!string.IsNullOrEmpty(ModuleState.ControlTitle))
{
title = ModuleState.ControlTitle;
}
}
return Task.CompletedTask;
}
}

View File

@ -32,12 +32,15 @@ namespace Oqtane.Themes
if (Resources != null && Resources.Exists(item => item.ResourceType == ResourceType.Script))
{
var scripts = new List<object>();
foreach (Resource resource in Resources.Where(item => item.ResourceType == ResourceType.Script))
foreach (Resource resource in Resources.Where(item => item.ResourceType == ResourceType.Script && item.Declaration != ResourceDeclaration.Global))
{
scripts.Add(new { href = resource.Url, bundle = resource.Bundle ?? "", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "" });
}
var interop = new Interop(JSRuntime);
await interop.IncludeScripts(scripts.ToArray());
if (scripts.Any())
{
var interop = new Interop(JSRuntime);
await interop.IncludeScripts(scripts.ToArray());
}
}
}
}

View File

@ -19,7 +19,7 @@
{
_moduleState = Module; // passed in from Pane component
string container = _moduleState.ContainerType;
if (PageState.ModuleId != -1 && PageState.Action != "" && _moduleState.UseAdminContainer)
if (PageState.ModuleId != -1 && _moduleState.UseAdminContainer)
{
container = Constants.DefaultAdminContainer;
}

View File

@ -119,7 +119,7 @@
<div class="row">
<div class="mx-auto text-center">
<button type="button" class="btn btn-success" @onclick="Install">Install Now</button><br /><br />
@((MarkupString) _message)
<ModuleMessage Message="@_message" Type="MessageType.Error"></ModuleMessage>
</div>
<div class="app-progress-indicator" style="@_loadingDisplay"></div>
</div>
@ -129,13 +129,13 @@
private string _databaseType = "LocalDB";
private string _serverName = "(LocalDb)\\MSSQLLocalDB";
private string _databaseName = "Oqtane-" + DateTime.UtcNow.ToString("yyyyMMddHHmm");
private string _username = "";
private string _password = "";
private string _hostUsername = Constants.HostUser;
private string _hostPassword = "";
private string _confirmPassword = "";
private string _hostEmail = "";
private string _message = "";
private string _username = string.Empty;
private string _password = string.Empty;
private string _hostUsername = UserNames.Host;
private string _hostPassword = string.Empty;
private string _confirmPassword = string.Empty;
private string _hostEmail = string.Empty;
private string _message = string.Empty;
private string _integratedSecurityDisplay = "display: none;";
private string _loadingDisplay = "display: none;";
@ -188,8 +188,8 @@
Aliases = uri.Authority,
HostEmail = _hostEmail,
HostPassword = _hostPassword,
HostName = Constants.HostUser,
TenantName = Constants.MasterTenant,
HostName = UserNames.Host,
TenantName = TenantNames.Master,
IsNewTenant = true,
SiteName = Constants.DefaultSite
};
@ -201,13 +201,13 @@
}
else
{
_message = "<div class=\"alert alert-danger\" role=\"alert\">" + installation.Message + "</div>";
_message = installation.Message;
_loadingDisplay = "display: none;";
}
}
else
{
_message = "<div class=\"alert alert-danger\" role=\"alert\">Please Enter All Fields And Ensure Passwords Match And Are Greater Than 5 Characters In Length</div>";
_message = "Please Enter All Fields And Ensure Passwords Match And Are Greater Than 5 Characters In Length";
}
}

View File

@ -1,8 +1,7 @@
@namespace Oqtane.UI
<ModuleMessage Message="@_message" Type="MessageType.Error" />
<ModuleMessage Message="@_message" Type="@_messagetype" />
<CascadingValue Value="this">
<ModuleMessage @ref="ModuleMessage" />
@DynamicComponent
</CascadingValue>
@if (_progressindicator)
@ -12,6 +11,7 @@
@code {
private string _message;
private MessageType _messagetype;
private bool _progressindicator = false;
[CascadingParameter]
@ -49,11 +49,13 @@
{
// module does not exist with typename specified
_message = "Module Does Not Have A Component Named " + Utilities.GetTypeNameLastSegment(typename, 0) + ".razor";
_messagetype = MessageType.Error;
}
}
else
{
_message = "Something is wrong with moduletype";
_messagetype = MessageType.Error;
}
};
@ -61,9 +63,10 @@
public void AddModuleMessage(string message, MessageType type)
{
_message = message;
_messagetype = type;
_progressindicator = false;
StateHasChanged();
ModuleMessage.SetModuleMessage(message, type);
}
public void ShowProgressIndicator()

View File

@ -14,10 +14,11 @@ namespace Oqtane.UI
public List<Module> Modules { get; set; }
public Uri Uri { get; set; }
public Dictionary<string, string> QueryString { get; set; }
public string UrlParameters { get; set; }
public int ModuleId { get; set; }
public string Action { get; set; }
public bool EditMode { get; set; }
public DateTime LastSyncDate { get; set; }
public Runtime Runtime { get; set; }
}
}
}

View File

@ -1,18 +1,24 @@
@namespace Oqtane.UI
@using Microsoft.AspNetCore.Components.Rendering
@namespace Oqtane.UI
@inject IUserService UserService
@inject IModuleService ModuleService
@inject IModuleDefinitionService ModuleDefinitionService
<div class="@_paneadminborder">
@if (_panetitle != "")
{
@if (_useadminborder)
{
<div class="@_paneadminborder">
@((MarkupString)_panetitle)
}
@DynamicComponent
</div>
}
else
{
@DynamicComponent
</div>
}
@code {
private string _paneadminborder = "";
private bool _useadminborder = false;
private string _paneadminborder = "container";
private string _panetitle = "";
[CascadingParameter]
@ -25,8 +31,9 @@
protected override void OnParametersSet()
{
if (PageState.EditMode && !PageState.Page.EditMode && UserSecurity.IsAuthorized(PageState.User,PermissionNames.Edit, PageState.Page.Permissions) && Name != Constants.AdminPane)
if (PageState.EditMode && UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions) && Name != PaneNames.Admin)
{
_useadminborder = true;
_paneadminborder = "app-pane-admin-border";
_panetitle = "<div class=\"app-pane-admin-title\">" + Name + " Pane</div>";
}
@ -38,12 +45,12 @@
DynamicComponent = builder =>
{
if (PageState.ModuleId != -1 && PageState.Action != "")
if (PageState.ModuleId != -1 && PageState.Action != Constants.DefaultAction)
{
if (Name.ToLower() == Constants.AdminPane.ToLower())
if (Name.ToLower() == PaneNames.Admin.ToLower())
{
Module module = PageState.Modules.FirstOrDefault(item => item.ModuleId == PageState.ModuleId);
if (module != null)
if (module != null && !module.IsDeleted)
{
var typename = module.ModuleType;
// check for core module actions component
@ -51,7 +58,7 @@
{
typename = Constants.DefaultModuleActionsTemplate.Replace(Constants.ActionToken, PageState.Action);
}
var moduleType = Type.GetType(typename);
if (moduleType != null)
{
@ -74,24 +81,17 @@
authorized = UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, module.Permissions);
break;
case SecurityAccessLevel.Admin:
authorized = UserSecurity.IsAuthorized(PageState.User, Constants.AdminRole);
authorized = UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin);
break;
case SecurityAccessLevel.Host:
authorized = UserSecurity.IsAuthorized(PageState.User, Constants.HostRole);
authorized = UserSecurity.IsAuthorized(PageState.User, RoleNames.Host);
break;
}
}
if (authorized)
{
if (!Constants.DefaultModuleActions.Contains(PageState.Action) && module.ControlTitle != "")
{
module.Title = module.ControlTitle;
}
builder.OpenComponent(0, Type.GetType(Constants.ContainerComponent));
builder.AddAttribute(1, "Module", module);
builder.CloseComponent();
CreateComponent(builder, module);
}
}
else
@ -106,14 +106,12 @@
if (PageState.ModuleId != -1)
{
Module module = PageState.Modules.FirstOrDefault(item => item.ModuleId == PageState.ModuleId);
if (module != null && module.Pane.ToLower() == Name.ToLower())
if (module != null && module.Pane.ToLower() == Name.ToLower() && !module.IsDeleted)
{
// check if user is authorized to view module
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, module.Permissions))
{
builder.OpenComponent(0, Type.GetType(Constants.ContainerComponent));
builder.AddAttribute(1, "Module", module);
builder.CloseComponent();
CreateComponent(builder, module);
}
}
}
@ -124,14 +122,19 @@
// check if user is authorized to view module
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, module.Permissions))
{
builder.OpenComponent(0, Type.GetType(Constants.ContainerComponent));
builder.AddAttribute(1, "Module", module);
builder.SetKey(module.PageModuleId);
builder.CloseComponent();
CreateComponent(builder, module);
}
}
}
}
};
}
}
private void CreateComponent(RenderTreeBuilder builder, Module module)
{
builder.OpenComponent(0, Type.GetType(Constants.ContainerComponent));
builder.AddAttribute(1, "Module", module);
builder.SetKey(module.PageModuleId);
builder.CloseComponent();
}
}

View File

@ -1,4 +1,4 @@
@using System.Diagnostics.CodeAnalysis
@using System.Diagnostics.CodeAnalysis
@using System.Runtime.InteropServices
@namespace Oqtane.UI
@inject AuthenticationStateProvider AuthenticationStateProvider
@ -76,7 +76,8 @@
User user = null;
List<Module> modules;
var moduleid = -1;
var action = "";
var action = Constants.DefaultAction;
var urlparameters = string.Empty;
var editmode = false;
var reload = Reload.None;
var lastsyncdate = DateTime.UtcNow;
@ -179,21 +180,55 @@
// extract admin route elements from path
var segments = path.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
int result;
// check if path has moduleid and action specification ie. pagename/moduleid/action/
if (segments.Length >= 2 && int.TryParse(segments[segments.Length - 2], out result))
int modIdPos = 0;
int actionPos = 0;
int urlParametersPos = 0;
for (int i = 0; i < segments.Length; i++)
{
action = segments[segments.Length - 1];
moduleid = result;
path = path.Replace(moduleid.ToString() + "/" + action + "/", "");
}
else
{
// check if path has moduleid specification ie. pagename/moduleid/
if (segments.Length >= 1 && int.TryParse(segments[segments.Length - 1], out result))
if (segments[i] == Constants.UrlParametersDelimiter)
{
moduleid = result;
path = path.Replace(moduleid.ToString() + "/", "");
urlParametersPos = i + 1;
}
if (i >= urlParametersPos && urlParametersPos != 0)
{
urlparameters += "/" + segments[i];
}
if (segments[i] == Constants.ModuleDelimiter)
{
modIdPos = i + 1;
actionPos = modIdPos + 1;
if (actionPos <= segments.Length - 1)
{
action = segments[actionPos];
}
}
}
// check if path has moduleid and action specification ie. pagename/*/moduleid/action/
if (modIdPos > 0)
{
int.TryParse(segments[modIdPos], out result);
moduleid = result;
if (actionPos > segments.Length - 1)
{
path = path.Replace(segments[modIdPos - 1] + "/" + segments[modIdPos] + "/", "");
}
else
{
path = path.Replace(segments[modIdPos - 1] + "/" + segments[modIdPos] + "/" + segments[actionPos] + "/", "");
}
}
if (urlParametersPos > 0)
{
path = path.Replace(segments[urlParametersPos - 1] + urlparameters + "/", "");
}
// remove trailing slash so it can be used as a key for Pages
@ -220,17 +255,14 @@
{
page = pages.Where(item => item.Path == path).FirstOrDefault();
reload = Reload.Page;
if (page != null)
{
editmode = page.EditMode;
}
editmode = false;
}
if (page != null)
{
if (PageState == null)
{
editmode = page.EditMode;
editmode = false;
}
// check if user is authorized to view page
@ -243,15 +275,15 @@
reload = Reload.Page;
}
if (PageState == null || reload >= Reload.Page)
if (PageState == null || reload >= Reload.Site)
{
modules = await ModuleService.GetModulesAsync(site.SiteId);
(page, modules) = ProcessModules(page, modules, moduleid, action, (!string.IsNullOrEmpty(page.DefaultContainerType)) ? page.DefaultContainerType : site.DefaultContainerType);
}
else
{
modules = PageState.Modules;
}
(page, modules) = ProcessModules(page, modules, moduleid, action, (!string.IsNullOrEmpty(page.DefaultContainerType)) ? page.DefaultContainerType : site.DefaultContainerType);
_pagestate = new PageState
{
@ -263,6 +295,7 @@
Modules = modules,
Uri = new Uri(_absoluteUri, UriKind.Absolute),
QueryString = querystring,
UrlParameters = urlparameters,
ModuleId = moduleid,
Action = action,
EditMode = editmode,
@ -352,6 +385,10 @@
if (string.IsNullOrEmpty(page.ThemeType))
{
page.ThemeType = site.DefaultThemeType;
}
if (string.IsNullOrEmpty(page.LayoutType))
{
page.LayoutType = site.DefaultLayoutType;
}
@ -360,6 +397,13 @@
string panes = "";
Type themetype = Type.GetType(page.ThemeType);
if (themetype == null)
{
// fallback
page.ThemeType = Constants.DefaultTheme;
page.LayoutType = Constants.DefaultLayout;
themetype = Type.GetType(Constants.DefaultTheme);
}
if (themetype != null)
{
var themeobject = Activator.CreateInstance(themetype) as IThemeControl;
@ -398,21 +442,28 @@
var paneindex = new Dictionary<string, int>();
foreach (Module module in modules)
{
if (module.PageId == page.PageId || module.ModuleId == moduleid)
// initialize module control properties
module.SecurityAccessLevel = SecurityAccessLevel.Host;
module.ControlTitle = "";
module.Actions = "";
module.UseAdminContainer = false;
module.PaneModuleIndex = -1;
module.PaneModuleCount = 0;
if ((module.PageId == page.PageId || module.ModuleId == moduleid))
{
var typename = string.Empty;
if (module.ModuleDefinition != null)
var typename = Constants.ErrorModule;
if (module.ModuleDefinition != null && (module.ModuleDefinition.Runtimes == "" || module.ModuleDefinition.Runtimes.Contains(GetRuntime().ToString())))
{
typename = module.ModuleDefinition.ControlTypeTemplate;
}
else
{
typename = Constants.ErrorModule;
}
if (module.ModuleId == moduleid && action != "")
{
// check if the module defines custom routes
// handle default action
if (action == Constants.DefaultAction && !string.IsNullOrEmpty(module.ModuleDefinition.DefaultAction))
{
action = module.ModuleDefinition.DefaultAction;
}
// check if the module defines custom action routes
if (module.ModuleDefinition.ControlTypeRoutes != "")
{
foreach (string route in module.ModuleDefinition.ControlTypeRoutes.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries))
@ -423,13 +474,10 @@
}
}
}
module.ModuleType = typename.Replace(Constants.ActionToken, action);
}
else
{
module.ModuleType = typename.Replace(Constants.ActionToken, Constants.DefaultAction);
}
module.ModuleType = typename.Replace(Constants.ActionToken, action);
// get additional metadata from IModuleControl interface
typename = module.ModuleType;
if (Constants.DefaultModuleActions.Contains(action))
@ -462,7 +510,7 @@
// ensure module's pane exists in current page and if not, assign it to the Admin pane
if (page.Panes == null || page.Panes.FindIndex(item => item.Equals(module.Pane, StringComparison.OrdinalIgnoreCase)) == -1)
{
module.Pane = Constants.AdminPane;
module.Pane = PaneNames.Admin;
}
// calculate module position within pane
@ -477,6 +525,7 @@
module.PaneModuleIndex = paneindex[module.Pane.ToLower()];
// container fallback
if (string.IsNullOrEmpty(module.ContainerType))
{
module.ContainerType = defaultcontainertype;
@ -486,7 +535,10 @@
foreach (Module module in modules.Where(item => item.PageId == page.PageId))
{
module.PaneModuleCount = paneindex[module.Pane.ToLower()] + 1;
if (paneindex.ContainsKey(module.Pane.ToLower()))
{
module.PaneModuleCount = paneindex[module.Pane.ToLower()] + 1;
}
}
return (page, modules);
@ -512,4 +564,4 @@
=> RuntimeInformation.IsOSPlatform(OSPlatform.Create("BROWSER"))
? Runtime.WebAssembly
: Runtime.Server;
}
}

View File

@ -1,4 +1,4 @@
@namespace Oqtane.UI
@namespace Oqtane.UI
@inject IJSRuntime JsRuntime
@inject NavigationManager NavigationManager
@ -33,11 +33,14 @@
// manage stylesheets for this page
string batch = DateTime.Now.ToString("yyyyMMddHHmmssfff");
var links = new List<object>();
foreach (Resource resource in PageState.Page.Resources.Where(item => item.ResourceType == ResourceType.Stylesheet))
foreach (Resource resource in PageState.Page.Resources.Where(item => item.ResourceType == ResourceType.Stylesheet && item.Declaration != ResourceDeclaration.Global))
{
links.Add(new { id = "app-stylesheet-" + batch + "-" + (links.Count + 1).ToString("00"), rel = "stylesheet", href = resource.Url, type = "text/css", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", key = "" });
}
await interop.IncludeLinks(links.ToArray());
if (links.Any())
{
await interop.IncludeLinks(links.ToArray());
}
await interop.RemoveElementsById("app-stylesheet", "", "app-stylesheet-" + batch + "-00");
// add favicon
@ -46,7 +49,7 @@
await interop.IncludeLink("app-favicon", "shortcut icon", Utilities.ContentUrl(PageState.Alias, PageState.Site.FaviconFileId.Value), "image/x-icon", "", "", "id");
}
// add PWA support
if (PageState.Site.PwaIsEnabled)
if (PageState.Site.PwaIsEnabled && PageState.Site.PwaAppIconFileId != null && PageState.Site.PwaSplashIconFileId != null)
{
await InitializePwa(interop);
}
@ -54,11 +57,6 @@
DynamicComponent = builder =>
{
var themeType = Type.GetType(PageState.Page.ThemeType);
if (themeType == null)
{
// fallback
themeType = Type.GetType(Constants.DefaultTheme);
}
builder.OpenComponent(0, themeType);
builder.CloseComponent();
};
@ -84,7 +82,7 @@
"\"sizes\": \"512x512\", " +
"\"type\": \"image/png\" " +
"}] " +
"} " +
"}; " +
"const serialized = JSON.stringify(manifest); " +
"const blob = new Blob([serialized], {type: 'application/javascript'}); " +
"const url = URL.createObjectURL(blob); " +

View File

@ -1,7 +1,8 @@
@using System
@using System
@using System.Linq
@using System.Collections.Generic
@using System.Net.Http
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.AspNetCore.Components.Routing

View File

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>Oqtane.Client</id>
<version>1.0.1</version>
<version>2.0.0</version>
<authors>Shaun Walker</authors>
<owners>.NET Foundation</owners>
<title>Oqtane Framework</title>
@ -13,11 +13,11 @@
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
<iconUrl>https://www.oqtane.org/Portals/0/icon.jpg</iconUrl>
<tags>oqtane</tags>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v1.0.1</releaseNotes>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.0</releaseNotes>
<summary>A modular application framework for Blazor</summary>
</metadata>
<files>
<file src="..\Oqtane.Client\bin\Release\netstandard2.1\Oqtane.Client.dll" target="lib\netstandard2.1" />
<file src="..\Oqtane.Client\bin\Release\netstandard2.1\Oqtane.Client.pdb" target="lib\netstandard2.1" />
<file src="..\Oqtane.Client\bin\Release\net5.0\Oqtane.Client.dll" target="lib\net5.0" />
<file src="..\Oqtane.Client\bin\Release\net5.0\Oqtane.Client.pdb" target="lib\net5.0" />
</files>
</package>

View File

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>Oqtane.Framework</id>
<version>1.0.1</version>
<version>2.0.0</version>
<authors>Shaun Walker</authors>
<owners>.NET Foundation</owners>
<title>Oqtane Framework</title>
@ -13,16 +13,11 @@
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
<iconUrl>https://www.oqtane.org/Portals/0/icon.jpg</iconUrl>
<tags>oqtane framework</tags>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v1.0.1</releaseNotes>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.0</releaseNotes>
<summary>A modular application framework for Blazor</summary>
</metadata>
<files>
<file src="..\Oqtane.Client\bin\Release\netstandard2.1\Oqtane.Client.dll" target="lib\netcoreapp3.1" />
<file src="..\Oqtane.Client\bin\Release\netstandard2.1\Oqtane.Client.pdb" target="lib\netcoreapp3.1" />
<file src="..\Oqtane.Server\bin\Release\netcoreapp3.1\Oqtane.Server.dll" target="lib\netcoreapp3.1" />
<file src="..\Oqtane.Server\bin\Release\netcoreapp3.1\Oqtane.Server.pdb" target="lib\netcoreapp3.1" />
<file src="..\Oqtane.Shared\bin\Release\netstandard2.1\Oqtane.Shared.dll" target="lib\netcoreapp3.1" />
<file src="..\Oqtane.Shared\bin\Release\netstandard2.1\Oqtane.Shared.pdb" target="lib\netcoreapp3.1" />
<file src="..\Oqtane.Server\wwwroot\**\*.*" target="wwwroot" />
<file src="..\Oqtane.Server\bin\Release\net5.0\publish\*.*" target="lib\net5.0" />
<file src="..\Oqtane.Server\bin\Release\net5.0\publish\wwwroot\**\*.*" target="wwwroot" />
</files>
</package>

View File

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>Oqtane.Server</id>
<version>1.0.1</version>
<version>2.0.0</version>
<authors>Shaun Walker</authors>
<owners>.NET Foundation</owners>
<title>Oqtane Framework</title>
@ -13,11 +13,11 @@
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
<iconUrl>https://www.oqtane.org/Portals/0/icon.jpg</iconUrl>
<tags>oqtane</tags>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v1.0.1</releaseNotes>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.0</releaseNotes>
<summary>A modular application framework for Blazor</summary>
</metadata>
<files>
<file src="..\Oqtane.Server\bin\Release\netcoreapp3.1\Oqtane.Server.dll" target="lib\netcoreapp3.1" />
<file src="..\Oqtane.Server\bin\Release\netcoreapp3.1\Oqtane.Server.pdb" target="lib\netcoreapp3.1" />
<file src="..\Oqtane.Server\bin\Release\net5.0\Oqtane.Server.dll" target="lib\net5.0" />
<file src="..\Oqtane.Server\bin\Release\net5.0\Oqtane.Server.pdb" target="lib\net5.0" />
</files>
</package>

View File

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>Oqtane.Shared</id>
<version>1.0.1</version>
<version>2.0.0</version>
<authors>Shaun Walker</authors>
<owners>.NET Foundation</owners>
<title>Oqtane Framework</title>
@ -13,11 +13,11 @@
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
<iconUrl>https://www.oqtane.org/Portals/0/icon.jpg</iconUrl>
<tags>oqtane</tags>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v1.0.1</releaseNotes>
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v2.0.0</releaseNotes>
<summary>A modular application framework for Blazor</summary>
</metadata>
<files>
<file src="..\Oqtane.Shared\bin\Release\netstandard2.1\Oqtane.Shared.dll" target="lib\netstandard2.1" />
<file src="..\Oqtane.Shared\bin\Release\netstandard2.1\Oqtane.Shared.pdb" target="lib\netstandard2.1" />
<file src="..\Oqtane.Shared\bin\Release\net5.0\Oqtane.Shared.dll" target="lib\net5.0" />
<file src="..\Oqtane.Shared\bin\Release\net5.0\Oqtane.Shared.pdb" target="lib\net5.0" />
</files>
</package>

View File

@ -0,0 +1 @@
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net5.0\publish\*" -DestinationPath "..\Oqtane.Server\bin\Release\Oqtane.Framework.2.0.0.Install.zip" -Force

Binary file not shown.

View File

@ -1,5 +1,18 @@
DEL "*.nupkg"
del "*.nupkg"
dotnet clean -c Release ..\Oqtane.sln
dotnet build -c Release ..\Oqtane.sln
dotnet pack -o .\ -c Release ..\Oqtane.sln
nuget.exe pack Oqtane.Framework.nuspec
nuget.exe pack Oqtane.Client.nuspec
nuget.exe pack Oqtane.Server.nuspec
nuget.exe pack Oqtane.Shared.nuspec
del /F/Q/S "..\Oqtane.Server\bin\Release\net5.0\publish" > NUL
rmdir /Q/S "..\Oqtane.Server\bin\Release\net5.0\publish"
dotnet publish ..\Oqtane.Server\Oqtane.Server.csproj /p:Configuration=Release
del "..\Oqtane.Server\bin\Release\net5.0\publish\appsettings.json"
ren "..\Oqtane.Server\bin\Release\net5.0\publish\appsettings.release.json" "appsettings.json"
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe ".\install.ps1"
del "..\Oqtane.Server\bin\Release\net5.0\publish\appsettings.json"
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe ".\upgrade.ps1"
del "..\Oqtane.Server\bin\Release\net5.0\publish\Oqtane.Upgrade.*"
nuget.exe pack Oqtane.Framework.nuspec

View File

@ -0,0 +1 @@
Compress-Archive -Path "..\Oqtane.Server\bin\Release\net5.0\publish\*" -DestinationPath "..\Oqtane.Server\bin\Release\Oqtane.Framework.2.0.0.Upgrade.zip" -Force

View File

@ -14,7 +14,7 @@ using Microsoft.AspNetCore.Http;
namespace Oqtane.Controllers
{
[Route("{alias}/api/[controller]")]
[Route(ControllerRoutes.Default)]
public class AliasController : Controller
{
private readonly IAliasRepository _aliases;
@ -32,7 +32,7 @@ namespace Oqtane.Controllers
// GET: api/<controller>
[HttpGet]
[Authorize(Roles = Constants.AdminRole)]
[Authorize(Roles = RoleNames.Admin)]
public IEnumerable<Alias> Get()
{
return _aliases.GetAliases();
@ -40,7 +40,7 @@ namespace Oqtane.Controllers
// GET api/<controller>/5
[HttpGet("{id}")]
[Authorize(Roles = Constants.AdminRole)]
[Authorize(Roles = RoleNames.Admin)]
public Alias Get(int id)
{
return _aliases.GetAlias(id);
@ -86,7 +86,7 @@ namespace Oqtane.Controllers
// POST api/<controller>
[HttpPost]
[Authorize(Roles = Constants.AdminRole)]
[Authorize(Roles = RoleNames.Admin)]
public Alias Post([FromBody] Alias alias)
{
if (ModelState.IsValid)
@ -99,7 +99,7 @@ namespace Oqtane.Controllers
// PUT api/<controller>/5
[HttpPut("{id}")]
[Authorize(Roles = Constants.AdminRole)]
[Authorize(Roles = RoleNames.Admin)]
public Alias Put(int id, [FromBody] Alias alias)
{
if (ModelState.IsValid)
@ -112,7 +112,7 @@ namespace Oqtane.Controllers
// DELETE api/<controller>/5
[HttpDelete("{id}")]
[Authorize(Roles = Constants.AdminRole)]
[Authorize(Roles = RoleNames.Admin)]
public void Delete(int id)
{
_aliases.DeleteAlias(id);

View File

@ -1,4 +1,4 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
@ -22,7 +22,7 @@ using Microsoft.AspNetCore.Routing.Constraints;
namespace Oqtane.Controllers
{
[Route("{alias}/api/[controller]")]
[Route(ControllerRoutes.Default)]
public class FileController : Controller
{
private readonly IWebHostEnvironment _environment;
@ -58,7 +58,7 @@ namespace Oqtane.Controllers
}
else
{
if (User.IsInRole(Constants.HostRole))
if (User.IsInRole(RoleNames.Host))
{
folder = GetFolderPath(folder);
if (Directory.Exists(folder))
@ -132,11 +132,23 @@ namespace Oqtane.Controllers
// PUT api/<controller>/5
[HttpPut("{id}")]
[Authorize(Roles = Constants.RegisteredRole)]
[Authorize(Roles = RoleNames.Registered)]
public Models.File Put(int id, [FromBody] Models.File file)
{
if (ModelState.IsValid && _userPermissions.IsAuthorized(User, EntityNames.Folder, file.Folder.FolderId, PermissionNames.Edit))
if (ModelState.IsValid && _userPermissions.IsAuthorized(User, EntityNames.Folder, file.FolderId, PermissionNames.Edit))
{
file.Folder = _folders.GetFolder(file.FolderId);
Models.File _file = _files.GetFile(id, false);
if (_file.Name != file.Name || _file.FolderId != file.FolderId)
{
string folderpath = GetFolderPath(file.Folder);
if (!Directory.Exists(folderpath))
{
Directory.CreateDirectory(folderpath);
}
System.IO.File.Move(Path.Combine(GetFolderPath(_file.Folder), _file.Name), Path.Combine(folderpath, file.Name));
}
file.Extension = Path.GetExtension(file.Name).ToLower().Replace(".", "");
file = _files.UpdateFile(file);
_logger.Log(LogLevel.Information, this, LogFunction.Update, "File Updated {File}", file);
}
@ -152,7 +164,7 @@ namespace Oqtane.Controllers
// DELETE api/<controller>/5
[HttpDelete("{id}")]
[Authorize(Roles = Constants.RegisteredRole)]
[Authorize(Roles = RoleNames.Registered)]
public void Delete(int id)
{
Models.File file = _files.GetFile(id);
@ -270,7 +282,7 @@ namespace Oqtane.Controllers
}
else
{
if (User.IsInRole(Constants.HostRole))
if (User.IsInRole(RoleNames.Host))
{
folderPath = GetFolderPath(folder);
}
@ -432,8 +444,8 @@ namespace Oqtane.Controllers
string filepath = Path.Combine(GetFolderPath(file.Folder), file.Name);
if (System.IO.File.Exists(filepath))
{
byte[] filebytes = System.IO.File.ReadAllBytes(filepath);
return File(filebytes, "application/octet-stream", file.Name);
var stream = new FileStream(filepath, FileMode.Open);
return File(stream, "application/octet-stream", file.Name);
}
else
{
@ -441,8 +453,8 @@ namespace Oqtane.Controllers
HttpContext.Response.StatusCode = 404;
if (System.IO.File.Exists(errorpath))
{
byte[] filebytes = System.IO.File.ReadAllBytes(errorpath);
return File(filebytes, "application/octet-stream", file.Name);
var stream = new FileStream(errorpath, FileMode.Open);
return File(stream, "application/octet-stream", file.Name);
}
}
}
@ -450,16 +462,16 @@ namespace Oqtane.Controllers
{
_logger.Log(LogLevel.Error, this, LogFunction.Read, "User Not Authorized To Access File {FileId}", id);
HttpContext.Response.StatusCode = 401;
byte[] filebytes = System.IO.File.ReadAllBytes(errorpath);
return File(filebytes, "application/octet-stream", file.Name);
var stream = new FileStream(errorpath, FileMode.Open);
return File(stream, "application/octet-stream", file.Name);
}
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Read, "File Not Found {FileId}", id);
HttpContext.Response.StatusCode = 404;
byte[] filebytes = System.IO.File.ReadAllBytes(errorpath);
return File(filebytes, "application/octet-stream", "error.png");
var stream = new FileStream(errorpath, FileMode.Open);
return File(stream, "application/octet-stream", file.Name);
}
return null;
}
@ -483,7 +495,7 @@ namespace Oqtane.Controllers
string[] folders = folderpath.Split(separators, StringSplitOptions.RemoveEmptyEntries);
foreach (string folder in folders)
{
path = Utilities.PathCombine(path, folder, "\\");
path = Utilities.PathCombine(path, folder, Path.DirectorySeparatorChar.ToString());
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);

View File

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.IO;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using Oqtane.Models;
@ -10,20 +11,25 @@ using Oqtane.Extensions;
using Oqtane.Infrastructure;
using Oqtane.Repository;
using Oqtane.Security;
using Microsoft.AspNetCore.Hosting;
namespace Oqtane.Controllers
{
[Route("{alias}/api/[controller]")]
[Route(ControllerRoutes.Default)]
public class FolderController : Controller
{
private readonly IWebHostEnvironment _environment;
private readonly IFolderRepository _folders;
private readonly IUserPermissions _userPermissions;
private readonly ITenantResolver _tenants;
private readonly ILogManager _logger;
public FolderController(IFolderRepository folders, IUserPermissions userPermissions, ILogManager logger)
public FolderController(IWebHostEnvironment environment, IFolderRepository folders, IUserPermissions userPermissions, ITenantResolver tenants, ILogManager logger)
{
_environment = environment;
_folders = folders;
_userPermissions = userPermissions;
_tenants = tenants;
_logger = logger;
}
@ -87,7 +93,7 @@ namespace Oqtane.Controllers
// POST api/<controller>
[HttpPost]
[Authorize(Roles = Constants.RegisteredRole)]
[Authorize(Roles = RoleNames.Registered)]
public Folder Post([FromBody] Folder folder)
{
if (ModelState.IsValid)
@ -100,7 +106,7 @@ namespace Oqtane.Controllers
else
{
permissions = new List<Permission> {
new Permission(PermissionNames.Edit, Constants.AdminRole, true),
new Permission(PermissionNames.Edit, RoleNames.Admin, true),
}.EncodePermissions();
}
if (_userPermissions.IsAuthorized(User, PermissionNames.Edit, permissions))
@ -112,7 +118,7 @@ namespace Oqtane.Controllers
Folder parent = _folders.GetFolder(folder.ParentId.Value);
folder.Path = Utilities.PathCombine(parent.Path, folder.Name);
}
folder.Path = Utilities.PathCombine(folder.Path, "\\");
folder.Path = Utilities.PathCombine(folder.Path, Path.DirectorySeparatorChar.ToString());
folder = _folders.AddFolder(folder);
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Folder Added {Folder}", folder);
}
@ -135,19 +141,26 @@ namespace Oqtane.Controllers
// PUT api/<controller>/5
[HttpPut("{id}")]
[Authorize(Roles = Constants.RegisteredRole)]
[Authorize(Roles = RoleNames.Registered)]
public Folder Put(int id, [FromBody] Folder folder)
{
if (ModelState.IsValid && _userPermissions.IsAuthorized(User, EntityNames.Folder, folder.FolderId, PermissionNames.Edit))
{
if (folder.IsPathValid())
{
if (string.IsNullOrEmpty(folder.Path) && folder.ParentId != null)
if (folder.ParentId != null)
{
Folder parent = _folders.GetFolder(folder.ParentId.Value);
folder.Path = Utilities.PathCombine(parent.Path, folder.Name);
}
folder.Path = Utilities.PathCombine(folder.Path, "\\");
folder.Path = Utilities.PathCombine(folder.Path, Path.DirectorySeparatorChar.ToString());
Models.Folder _folder = _folders.GetFolder(id, false);
if (_folder.Path != folder.Path && Directory.Exists(GetFolderPath(_folder)))
{
Directory.Move(GetFolderPath(_folder), GetFolderPath(folder));
}
folder = _folders.UpdateFolder(folder);
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Folder Updated {Folder}", folder);
}
@ -169,7 +182,7 @@ namespace Oqtane.Controllers
// PUT api/<controller>/?siteid=x&folderid=y&parentid=z
[HttpPut]
[Authorize(Roles = Constants.RegisteredRole)]
[Authorize(Roles = RoleNames.Registered)]
public void Put(int siteid, int folderid, int? parentid)
{
if (_userPermissions.IsAuthorized(User, EntityNames.Folder, folderid, PermissionNames.Edit))
@ -196,11 +209,16 @@ namespace Oqtane.Controllers
// DELETE api/<controller>/5
[HttpDelete("{id}")]
[Authorize(Roles = Constants.RegisteredRole)]
[Authorize(Roles = RoleNames.Registered)]
public void Delete(int id)
{
if (_userPermissions.IsAuthorized(User, EntityNames.Folder, id, PermissionNames.Edit))
{
Models.Folder _folder = _folders.GetFolder(id, false);
if (Directory.Exists(GetFolderPath(_folder)))
{
Directory.Delete(GetFolderPath(_folder));
}
_folders.DeleteFolder(id);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Folder Deleted {FolderId}", id);
}
@ -210,5 +228,10 @@ namespace Oqtane.Controllers
HttpContext.Response.StatusCode = 401;
}
}
private string GetFolderPath(Folder folder)
{
return Utilities.PathCombine(_environment.ContentRootPath, "Content", "Tenants", _tenants.GetTenant().TenantId.ToString(), "Sites", folder.SiteId.ToString(), folder.Path);
}
}
}

View File

@ -1,32 +1,36 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Oqtane.Models;
using Oqtane.Shared;
using Oqtane.Infrastructure;
using System;
using System.IO;
using System.Reflection;
using System.Linq;
using System.IO.Compression;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Oqtane.Infrastructure;
using Oqtane.Models;
using Oqtane.Modules;
using Oqtane.Shared;
using Oqtane.Themes;
using System.Diagnostics;
using Microsoft.Extensions.Caching.Memory;
namespace Oqtane.Controllers
{
[Route("{alias}/api/[controller]")]
[Route(ControllerRoutes.Default)]
public class InstallationController : Controller
{
private readonly IConfigurationRoot _config;
private readonly IInstallationManager _installationManager;
private readonly IDatabaseManager _databaseManager;
private readonly ILocalizationManager _localizationManager;
private readonly IMemoryCache _cache;
public InstallationController(IConfigurationRoot config, IInstallationManager installationManager, IDatabaseManager databaseManager)
public InstallationController(IConfigurationRoot config, IInstallationManager installationManager, IDatabaseManager databaseManager, ILocalizationManager localizationManager, IMemoryCache cache)
{
_config = config;
_installationManager = installationManager;
_databaseManager = databaseManager;
_localizationManager = localizationManager;
_cache = cache;
}
// POST api/<controller>
@ -35,7 +39,7 @@ namespace Oqtane.Controllers
{
var installation = new Installation {Success = false, Message = ""};
if (ModelState.IsValid && (User.IsInRole(Constants.HostRole) || string.IsNullOrEmpty(_config.GetConnectionString(SettingKeys.ConnectionStringKey))))
if (ModelState.IsValid && (User.IsInRole(RoleNames.Host) || string.IsNullOrEmpty(_config.GetConnectionString(SettingKeys.ConnectionStringKey))))
{
installation = _databaseManager.Install(config);
}
@ -56,7 +60,7 @@ namespace Oqtane.Controllers
}
[HttpGet("upgrade")]
[Authorize(Roles = Constants.HostRole)]
[Authorize(Roles = RoleNames.Host)]
public Installation Upgrade()
{
var installation = new Installation {Success = true, Message = ""};
@ -70,63 +74,7 @@ namespace Oqtane.Controllers
{
if (_config.GetSection("Runtime").Value == "WebAssembly")
{
// get list of assemblies which should be downloaded to browser
var assemblies = AppDomain.CurrentDomain.GetOqtaneClientAssemblies();
var list = assemblies.Select(a => a.GetName().Name).ToList();
// get module and theme dependencies
foreach (var assembly in assemblies)
{
foreach (var type in assembly.GetTypes().Where(item => item.GetInterfaces().Contains(typeof(IModule))))
{
var instance = Activator.CreateInstance(type) as IModule;
foreach (string name in instance.ModuleDefinition.Dependencies.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
if (!list.Contains(name)) list.Add(name);
}
}
foreach (var type in assembly.GetTypes().Where(item => item.GetInterfaces().Contains(typeof(ITheme))))
{
var instance = Activator.CreateInstance(type) as ITheme;
foreach (string name in instance.Theme.Dependencies.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
if (!list.Contains(name)) list.Add(name);
}
}
}
// create zip file containing assemblies and debug symbols
string binfolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
byte[] zipfile;
using (var memoryStream = new MemoryStream())
{
using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
{
ZipArchiveEntry entry;
foreach (string file in list)
{
entry = archive.CreateEntry(file + ".dll");
using (var filestream = new FileStream(Path.Combine(binfolder, file + ".dll"), FileMode.Open, FileAccess.Read))
using (var entrystream = entry.Open())
{
filestream.CopyTo(entrystream);
}
// include debug symbols ( we may want to consider restricting this to only host users or when running in debug mode for performance )
if (System.IO.File.Exists(Path.Combine(binfolder, file + ".pdb")))
{
entry = archive.CreateEntry(file + ".pdb");
using (var filestream = new FileStream(Path.Combine(binfolder, file + ".pdb"), FileMode.Open, FileAccess.Read))
using (var entrystream = entry.Open())
{
filestream.CopyTo(entrystream);
}
}
}
}
zipfile = memoryStream.ToArray();
}
return File(zipfile, "application/octet-stream", "oqtane.zip");
return File(GetAssemblies(), System.Net.Mime.MediaTypeNames.Application.Octet, "oqtane.zip");
}
else
{
@ -134,5 +82,86 @@ namespace Oqtane.Controllers
return null;
}
}
private byte[] GetAssemblies()
{
return _cache.GetOrCreate("assemblies", entry =>
{
// get list of assemblies which should be downloaded to client
var assemblies = AppDomain.CurrentDomain.GetOqtaneClientAssemblies();
var list = assemblies.Select(a => a.GetName().Name).ToList();
var binFolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
// insert satellite assemblies at beginning of list
foreach (var culture in _localizationManager.GetSupportedCultures())
{
var assembliesFolderPath = Path.Combine(binFolder, culture);
if (culture == Constants.DefaultCulture)
{
continue;
}
if (Directory.Exists(assembliesFolderPath))
{
foreach (var resourceFile in Directory.EnumerateFiles(assembliesFolderPath))
{
list.Insert(0, Path.Combine(culture, Path.GetFileNameWithoutExtension(resourceFile)));
}
}
else
{
Console.WriteLine($"The satellite assemblies folder named '{culture}' is not found.");
}
}
// insert module and theme dependencies at beginning of list
foreach (var assembly in assemblies)
{
foreach (var type in assembly.GetTypes().Where(item => item.GetInterfaces().Contains(typeof(IModule))))
{
var instance = Activator.CreateInstance(type) as IModule;
foreach (string name in instance.ModuleDefinition.Dependencies.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
if (!list.Contains(name)) list.Insert(0, name);
}
}
foreach (var type in assembly.GetTypes().Where(item => item.GetInterfaces().Contains(typeof(ITheme))))
{
var instance = Activator.CreateInstance(type) as ITheme;
foreach (string name in instance.Theme.Dependencies.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
if (!list.Contains(name)) list.Insert(0, name);
}
}
}
// create zip file containing assemblies and debug symbols
using (var memoryStream = new MemoryStream())
{
using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
{
foreach (string file in list)
{
using (var filestream = new FileStream(Path.Combine(binFolder, file + ".dll"), FileMode.Open, FileAccess.Read))
using (var entrystream = archive.CreateEntry(file + ".dll").Open())
{
filestream.CopyTo(entrystream);
}
// include debug symbols
if (System.IO.File.Exists(Path.Combine(binFolder, file + ".pdb")))
{
using (var filestream = new FileStream(Path.Combine(binFolder, file + ".pdb"), FileMode.Open, FileAccess.Read))
using (var entrystream = archive.CreateEntry(file + ".pdb").Open())
{
filestream.CopyTo(entrystream);
}
}
}
}
return memoryStream.ToArray();
}
});
}
}
}

View File

@ -12,7 +12,7 @@ using Oqtane.Repository;
namespace Oqtane.Controllers
{
[Route("{alias}/api/[controller]")]
[Route(ControllerRoutes.Default)]
public class JobController : Controller
{
private readonly IJobRepository _jobs;
@ -28,7 +28,7 @@ namespace Oqtane.Controllers
// GET: api/<controller>
[HttpGet]
[Authorize(Roles = Constants.HostRole)]
[Authorize(Roles = RoleNames.Host)]
public IEnumerable<Job> Get()
{
return _jobs.GetJobs();
@ -36,7 +36,7 @@ namespace Oqtane.Controllers
// GET api/<controller>/5
[HttpGet("{id}")]
[Authorize(Roles = Constants.HostRole)]
[Authorize(Roles = RoleNames.Host)]
public Job Get(int id)
{
return _jobs.GetJob(id);
@ -44,7 +44,7 @@ namespace Oqtane.Controllers
// POST api/<controller>
[HttpPost]
[Authorize(Roles = Constants.HostRole)]
[Authorize(Roles = RoleNames.Host)]
public Job Post([FromBody] Job job)
{
if (ModelState.IsValid)
@ -57,7 +57,7 @@ namespace Oqtane.Controllers
// PUT api/<controller>/5
[HttpPut("{id}")]
[Authorize(Roles = Constants.HostRole)]
[Authorize(Roles = RoleNames.Host)]
public Job Put(int id, [FromBody] Job job)
{
if (ModelState.IsValid)
@ -70,7 +70,7 @@ namespace Oqtane.Controllers
// DELETE api/<controller>/5
[HttpDelete("{id}")]
[Authorize(Roles = Constants.HostRole)]
[Authorize(Roles = RoleNames.Host)]
public void Delete(int id)
{
_jobs.DeleteJob(id);
@ -79,7 +79,7 @@ namespace Oqtane.Controllers
// GET api/<controller>/start
[HttpGet("start/{id}")]
[Authorize(Roles = Constants.HostRole)]
[Authorize(Roles = RoleNames.Host)]
public void Start(int id)
{
Job job = _jobs.GetJob(id);
@ -93,7 +93,7 @@ namespace Oqtane.Controllers
// GET api/<controller>/stop
[HttpGet("stop/{id}")]
[Authorize(Roles = Constants.HostRole)]
[Authorize(Roles = RoleNames.Host)]
public void Stop(int id)
{
Job job = _jobs.GetJob(id);

View File

@ -9,7 +9,7 @@ using Oqtane.Repository;
namespace Oqtane.Controllers
{
[Route("{alias}/api/[controller]")]
[Route(ControllerRoutes.Default)]
public class JobLogController : Controller
{
private readonly IJobLogRepository _jobLogs;
@ -23,7 +23,7 @@ namespace Oqtane.Controllers
// GET: api/<controller>
[HttpGet]
[Authorize(Roles = Constants.HostRole)]
[Authorize(Roles = RoleNames.Host)]
public IEnumerable<JobLog> Get()
{
return _jobLogs.GetJobLogs();
@ -31,7 +31,7 @@ namespace Oqtane.Controllers
// GET api/<controller>/5
[HttpGet("{id}")]
[Authorize(Roles = Constants.HostRole)]
[Authorize(Roles = RoleNames.Host)]
public JobLog Get(int id)
{
return _jobLogs.GetJobLog(id);
@ -39,7 +39,7 @@ namespace Oqtane.Controllers
// POST api/<controller>
[HttpPost]
[Authorize(Roles = Constants.HostRole)]
[Authorize(Roles = RoleNames.Host)]
public JobLog Post([FromBody] JobLog jobLog)
{
if (ModelState.IsValid)
@ -52,7 +52,7 @@ namespace Oqtane.Controllers
// PUT api/<controller>/5
[HttpPut("{id}")]
[Authorize(Roles = Constants.HostRole)]
[Authorize(Roles = RoleNames.Host)]
public JobLog Put(int id, [FromBody] JobLog jobLog)
{
if (ModelState.IsValid)
@ -65,7 +65,7 @@ namespace Oqtane.Controllers
// DELETE api/<controller>/5
[HttpDelete("{id}")]
[Authorize(Roles = Constants.HostRole)]
[Authorize(Roles = RoleNames.Host)]
public void Delete(int id)
{
_jobLogs.DeleteJobLog(id);

View File

@ -9,7 +9,7 @@ using Oqtane.Shared;
namespace Oqtane.Controllers
{
[Route("{alias}/api/[controller]")]
[Route(ControllerRoutes.Default)]
public class LogController : Controller
{
private readonly ILogManager _logger;
@ -23,7 +23,7 @@ namespace Oqtane.Controllers
// GET: api/<controller>?siteid=x&level=y&function=z&rows=50
[HttpGet]
[Authorize(Roles = Constants.AdminRole)]
[Authorize(Roles = RoleNames.Admin)]
public IEnumerable<Log> Get(string siteid, string level, string function, string rows)
{
return _logs.GetLogs(int.Parse(siteid), level, function, int.Parse(rows));
@ -31,7 +31,7 @@ namespace Oqtane.Controllers
// GET api/<controller>/5
[HttpGet("{id}")]
[Authorize(Roles = Constants.AdminRole)]
[Authorize(Roles = RoleNames.Admin)]
public Log Get(int id)
{
return _logs.GetLog(id);

View File

@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using Oqtane.Models;
@ -11,23 +11,29 @@ using Oqtane.Security;
namespace Oqtane.Controllers
{
[Route("{alias}/api/[controller]")]
[Route(ControllerRoutes.Default)]
public class ModuleController : Controller
{
private readonly IModuleRepository _modules;
private readonly IPageModuleRepository _pageModules;
private readonly IPageRepository _pages;
private readonly IModuleDefinitionRepository _moduleDefinitions;
private readonly ISettingRepository _settings;
private readonly IUserPermissions _userPermissions;
private readonly ITenantResolver _tenants;
private readonly ISyncManager _syncManager;
private readonly ILogManager _logger;
public ModuleController(IModuleRepository modules, IPageModuleRepository pageModules, IPageRepository pages, IModuleDefinitionRepository moduleDefinitions, IUserPermissions userPermissions, ILogManager logger)
public ModuleController(IModuleRepository modules, IPageModuleRepository pageModules, IPageRepository pages, IModuleDefinitionRepository moduleDefinitions, ISettingRepository settings, IUserPermissions userPermissions, ITenantResolver tenants, ISyncManager syncManager, ILogManager logger)
{
_modules = modules;
_modules = modules;
_pageModules = pageModules;
_pages = pages;
_moduleDefinitions = moduleDefinitions;
_settings = settings;
_userPermissions = userPermissions;
_tenants = tenants;
_syncManager = syncManager;
_logger = logger;
}
@ -36,6 +42,8 @@ namespace Oqtane.Controllers
public IEnumerable<Module> Get(string siteid)
{
List<ModuleDefinition> moduledefinitions = _moduleDefinitions.GetModuleDefinitions(int.Parse(siteid)).ToList();
List<Setting> settings = _settings.GetSettings(EntityNames.Module).ToList();
List<Module> modules = new List<Module>();
foreach (PageModule pagemodule in _pageModules.GetPageModules(int.Parse(siteid)))
{
@ -61,6 +69,8 @@ namespace Oqtane.Controllers
module.ContainerType = pagemodule.ContainerType;
module.ModuleDefinition = moduledefinitions.Find(item => item.ModuleDefinitionName == module.ModuleDefinitionName);
module.Settings = settings.Where(item => item.EntityId == pagemodule.ModuleId)
.ToDictionary(setting => setting.SettingName, setting => setting.SettingValue);
modules.Add(module);
}
@ -77,6 +87,9 @@ namespace Oqtane.Controllers
{
List<ModuleDefinition> moduledefinitions = _moduleDefinitions.GetModuleDefinitions(module.SiteId).ToList();
module.ModuleDefinition = moduledefinitions.Find(item => item.ModuleDefinitionName == module.ModuleDefinitionName);
module.Settings = _settings.GetSettings(EntityNames.Module, id)
.ToDictionary(setting => setting.SettingName, setting => setting.SettingValue);
return module;
}
else
@ -89,12 +102,13 @@ namespace Oqtane.Controllers
// POST api/<controller>
[HttpPost]
[Authorize(Roles = Constants.RegisteredRole)]
[Authorize(Roles = RoleNames.Registered)]
public Module Post([FromBody] Module module)
{
if (ModelState.IsValid && _userPermissions.IsAuthorized(User, EntityNames.Page, module.PageId, PermissionNames.Edit))
{
module = _modules.AddModule(module);
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.Site, _tenants.GetAlias().SiteId);
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Module Added {Module}", module);
}
else
@ -108,7 +122,7 @@ namespace Oqtane.Controllers
// PUT api/<controller>/5
[HttpPut("{id}")]
[Authorize(Roles = Constants.RegisteredRole)]
[Authorize(Roles = RoleNames.Registered)]
public Module Put(int id, [FromBody] Module module)
{
if (ModelState.IsValid && _userPermissions.IsAuthorized(User, EntityNames.Module, module.ModuleId, PermissionNames.Edit))
@ -122,12 +136,13 @@ namespace Oqtane.Controllers
var pages = _pages.GetPages(module.SiteId).ToList();
foreach (Page page in pages)
{
if (page.PageId != pageModule.PageId && !page.EditMode)
if (page.PageId != pageModule.PageId && !page.Path.StartsWith("admin/"))
{
_pageModules.AddPageModule(new PageModule { PageId = page.PageId, ModuleId = pageModule.ModuleId, Title = pageModule.Title, Pane = pageModule.Pane, Order = pageModule.Order, ContainerType = pageModule.ContainerType });
}
}
}
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.Site, _tenants.GetAlias().SiteId);
}
else
{
@ -140,12 +155,13 @@ namespace Oqtane.Controllers
// DELETE api/<controller>/5
[HttpDelete("{id}")]
[Authorize(Roles = Constants.RegisteredRole)]
[Authorize(Roles = RoleNames.Registered)]
public void Delete(int id)
{
if (_userPermissions.IsAuthorized(User, EntityNames.Module, id, PermissionNames.Edit))
{
_modules.DeleteModule(id);
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.Site, _tenants.GetAlias().SiteId);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Deleted {ModuleId}", id);
}
else
@ -157,7 +173,7 @@ namespace Oqtane.Controllers
// GET api/<controller>/export?moduleid=x
[HttpGet("export")]
[Authorize(Roles = Constants.RegisteredRole)]
[Authorize(Roles = RoleNames.Registered)]
public string Export(int moduleid)
{
string content = "";
@ -175,7 +191,7 @@ namespace Oqtane.Controllers
// POST api/<controller>/import?moduleid=x
[HttpPost("import")]
[Authorize(Roles = Constants.RegisteredRole)]
[Authorize(Roles = RoleNames.Registered)]
public bool Import(int moduleid, [FromBody] string content)
{
bool success = false;

View File

@ -0,0 +1,21 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Http;
using Oqtane.Infrastructure;
namespace Oqtane.Controllers
{
public class ModuleControllerBase : Controller
{
protected readonly ILogManager _logger;
protected int _entityId = -1; // passed as a querystring parameter for policy authorization and used for validation
public ModuleControllerBase(ILogManager logger, IHttpContextAccessor accessor)
{
_logger = logger;
if (accessor.HttpContext.Request.Query.ContainsKey("entityid"))
{
_entityId = int.Parse(accessor.HttpContext.Request.Query["entityid"]);
}
}
}
}

View File

@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using Oqtane.Models;
using Oqtane.Shared;
@ -15,10 +15,11 @@ using System;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using System.Xml.Linq;
using System.Text.Json;
namespace Oqtane.Controllers
{
[Route("{alias}/api/[controller]")]
[Route(ControllerRoutes.Default)]
public class ModuleDefinitionController : Controller
{
private readonly IModuleDefinitionRepository _moduleDefinitions;
@ -80,7 +81,7 @@ namespace Oqtane.Controllers
// PUT api/<controller>/5
[HttpPut("{id}")]
[Authorize(Roles = Constants.AdminRole)]
[Authorize(Roles = RoleNames.Admin)]
public void Put(int id, [FromBody] ModuleDefinition moduleDefinition)
{
if (ModelState.IsValid)
@ -91,16 +92,16 @@ namespace Oqtane.Controllers
}
[HttpGet("install")]
[Authorize(Roles = Constants.HostRole)]
[Authorize(Roles = RoleNames.Host)]
public void InstallModules()
{
_installationManager.InstallPackages("Modules", true);
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Modules Installed");
_installationManager.InstallPackages("Modules", true);
}
// DELETE api/<controller>/5?siteid=x
[HttpDelete("{id}")]
[Authorize(Roles = Constants.HostRole)]
[Authorize(Roles = RoleNames.Host)]
public void Delete(int id, int siteid)
{
ModuleDefinition moduledefinition = _moduleDefinitions.GetModuleDefinition(id, siteid);
@ -110,6 +111,7 @@ namespace Oqtane.Controllers
{
Type moduletype = Type.GetType(moduledefinition.ServerManagerType);
// execute uninstall logic
foreach (Tenant tenant in _tenants.GetTenants())
{
try
@ -130,25 +132,28 @@ namespace Oqtane.Controllers
_logger.Log(LogLevel.Error, this, LogFunction.Delete, "Error Uninstalling {ModuleDefinitionName} For Tenant {Tenant} {Error}", moduledefinition.ModuleDefinitionName, tenant.Name, ex.Message);
}
}
// use assets.json to clean up file resources
string assetfilepath = Path.Combine(_environment.WebRootPath, "Modules", Utilities.GetTypeName(moduledefinition.ModuleDefinitionName), "assets.json");
if (System.IO.File.Exists(assetfilepath))
{
List<string> assets = JsonSerializer.Deserialize<List<string>>(System.IO.File.ReadAllText(assetfilepath));
foreach(string asset in assets)
{
if (System.IO.File.Exists(asset))
{
System.IO.File.Delete(asset);
}
}
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Assets Removed For {ModuleDefinitionName}", moduledefinition.ModuleDefinitionName);
}
// clean up module static resource folder
string folder = Path.Combine(_environment.WebRootPath, Path.Combine("Modules", Utilities.GetTypeName(moduledefinition.ModuleDefinitionName)));
if (Directory.Exists(folder))
{
Directory.Delete(folder, true);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Static Resources Removed For {ModuleDefinitionName}", moduledefinition.ModuleDefinitionName);
}
// get root assembly name ( note that this only works if modules follow a specific naming convention for their assemblies )
string assemblyname = Utilities.GetAssemblyName(moduledefinition.ModuleDefinitionName).ToLower();
assemblyname = assemblyname.Replace(".client", "").Replace(".oqtane", "");
// remove module assemblies from /bin
string binfolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
foreach (string file in Directory.EnumerateFiles(binfolder, assemblyname + "*.*"))
{
System.IO.File.Delete(file);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Assembly {Filename} Removed For {ModuleDefinitionName}", file, moduledefinition.ModuleDefinitionName);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Resources Folder Removed For {ModuleDefinitionName}", moduledefinition.ModuleDefinitionName);
}
// remove module definition
@ -163,26 +168,26 @@ namespace Oqtane.Controllers
// POST api/<controller>?moduleid=x
[HttpPost]
[Authorize(Roles = Constants.HostRole)]
[Authorize(Roles = RoleNames.Host)]
public void Post([FromBody] ModuleDefinition moduleDefinition, string moduleid)
{
if (ModelState.IsValid)
{
string rootPath;
DirectoryInfo rootFolder = Directory.GetParent(_environment.ContentRootPath);
string templatePath = Utilities.PathCombine(_environment.WebRootPath, "Modules", "Templates", moduleDefinition.Template,"\\");
string templatePath = Utilities.PathCombine(_environment.WebRootPath, "Modules", "Templates", moduleDefinition.Template,Path.DirectorySeparatorChar.ToString());
if (moduleDefinition.Template == "internal")
{
rootPath = Utilities.PathCombine(rootFolder.FullName,"\\");
moduleDefinition.ModuleDefinitionName = moduleDefinition.Owner + "." + moduleDefinition.Name + "s, Oqtane.Client";
moduleDefinition.ServerManagerType = moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Manager." + moduleDefinition.Name + "Manager, Oqtane.Server";
rootPath = Utilities.PathCombine(rootFolder.FullName,Path.DirectorySeparatorChar.ToString());
moduleDefinition.ModuleDefinitionName = moduleDefinition.Owner + "." + moduleDefinition.Name + ", Oqtane.Client";
moduleDefinition.ServerManagerType = moduleDefinition.Owner + "." + moduleDefinition.Name + ".Manager." + moduleDefinition.Name + "Manager, Oqtane.Server";
}
else
{
rootPath = Utilities.PathCombine(rootFolder.Parent.FullName , moduleDefinition.Owner + "." + moduleDefinition.Name + "s","\\");
moduleDefinition.ModuleDefinitionName = moduleDefinition.Owner + "." + moduleDefinition.Name + "s, " + moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Client.Oqtane";
moduleDefinition.ServerManagerType = moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Manager." + moduleDefinition.Name + "Manager, " + moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Server.Oqtane";
rootPath = Utilities.PathCombine(rootFolder.Parent.FullName , moduleDefinition.Owner + "." + moduleDefinition.Name,Path.DirectorySeparatorChar.ToString());
moduleDefinition.ModuleDefinitionName = moduleDefinition.Owner + "." + moduleDefinition.Name + ", " + moduleDefinition.Owner + "." + moduleDefinition.Name + ".Client.Oqtane";
moduleDefinition.ServerManagerType = moduleDefinition.Owner + "." + moduleDefinition.Name + ".Manager." + moduleDefinition.Name + "Manager, " + moduleDefinition.Owner + "." + moduleDefinition.Name + ".Server.Oqtane";
}
ProcessTemplatesRecursively(new DirectoryInfo(templatePath), rootPath, rootFolder.Name, templatePath, moduleDefinition);
@ -196,8 +201,8 @@ namespace Oqtane.Controllers
{
// add embedded resources to project
List<string> resources = new List<string>();
resources.Add(Utilities.PathCombine("Modules", moduleDefinition.Owner + "." + moduleDefinition.Name + "s", "Scripts", moduleDefinition.Owner + "." + moduleDefinition.Name + "s.1.0.0.sql"));
resources.Add(Utilities.PathCombine("Modules", moduleDefinition.Owner + "." + moduleDefinition.Name + "s", "Scripts", moduleDefinition.Owner + "." + moduleDefinition.Name + "s.Uninstall.sql"));
resources.Add(Utilities.PathCombine("Modules", moduleDefinition.Owner + "." + moduleDefinition.Name, "Scripts", moduleDefinition.Owner + "." + moduleDefinition.Name + ".1.0.0.sql"));
resources.Add(Utilities.PathCombine("Modules", moduleDefinition.Owner + "." + moduleDefinition.Name, "Scripts", moduleDefinition.Owner + "." + moduleDefinition.Name + ".Uninstall.sql"));
EmbedResourceFiles(Utilities.PathCombine(rootPath, "Oqtane.Server", "Oqtane.Server.csproj"), resources);
}
@ -235,7 +240,20 @@ namespace Oqtane.Controllers
text = text.Replace("[ServerManagerType]", moduleDefinition.ServerManagerType);
text = text.Replace("[Folder]", folderPath);
text = text.Replace("[File]", Path.GetFileName(filePath));
text = text.Replace("[FrameworkVersion]", Constants.Version);
if (moduleDefinition.Version == "local")
{
text = text.Replace("[FrameworkVersion]", Constants.Version);
text = text.Replace("[ClientReference]", "<Reference Include=\"Oqtane.Client\"><HintPath>..\\..\\oqtane.framework\\Oqtane.Server\\bin\\Debug\\net5.0\\Oqtane.Client.dll</HintPath></Reference>");
text = text.Replace("[ServerReference]", "<Reference Include=\"Oqtane.Server\"><HintPath>..\\..\\oqtane.framework\\Oqtane.Server\\bin\\Debug\\net5.0\\Oqtane.Server.dll</HintPath></Reference>");
text = text.Replace("[SharedReference]", "<Reference Include=\"Oqtane.Shared\"><HintPath>..\\..\\oqtane.framework\\Oqtane.Server\\bin\\Debug\\net5.0\\Oqtane.Shared.dll</HintPath></Reference>");
}
else
{
text = text.Replace("[FrameworkVersion]", moduleDefinition.Version);
text = text.Replace("[ClientReference]", "<PackageReference Include=\"Oqtane.Client\" Version=\"" + moduleDefinition.Version + "\" />");
text = text.Replace("[ServerReference]", "<PackageReference Include=\"Oqtane.Server\" Version=\"" + moduleDefinition.Version + "\" />");
text = text.Replace("[SharedReference]", "<PackageReference Include=\"Oqtane.Shared\" Version=\"" + moduleDefinition.Version + "\" />");
}
System.IO.File.WriteAllText(filePath, text);
}

View File

@ -10,7 +10,7 @@ using Oqtane.Security;
namespace Oqtane.Controllers
{
[Route("{alias}/api/[controller]")]
[Route(ControllerRoutes.Default)]
public class NotificationController : Controller
{
private readonly INotificationRepository _notifications;
@ -26,7 +26,7 @@ namespace Oqtane.Controllers
// GET: api/<controller>?siteid=x&type=y&userid=z
[HttpGet]
[Authorize(Roles = Constants.RegisteredRole)]
[Authorize(Roles = RoleNames.Registered)]
public IEnumerable<Notification> Get(string siteid, string direction, string userid)
{
IEnumerable<Notification> notifications = null;
@ -46,7 +46,7 @@ namespace Oqtane.Controllers
// GET api/<controller>/5
[HttpGet("{id}")]
[Authorize(Roles = Constants.RegisteredRole)]
[Authorize(Roles = RoleNames.Registered)]
public Notification Get(int id)
{
Notification notification = _notifications.GetNotification(id);
@ -59,7 +59,7 @@ namespace Oqtane.Controllers
// POST api/<controller>
[HttpPost]
[Authorize(Roles = Constants.RegisteredRole)]
[Authorize(Roles = RoleNames.Registered)]
public Notification Post([FromBody] Notification notification)
{
if (IsAuthorized(notification.FromUserId))
@ -72,7 +72,7 @@ namespace Oqtane.Controllers
// PUT api/<controller>/5
[HttpPut("{id}")]
[Authorize(Roles = Constants.RegisteredRole)]
[Authorize(Roles = RoleNames.Registered)]
public Notification Put(int id, [FromBody] Notification notification)
{
if (IsAuthorized(notification.FromUserId))
@ -85,7 +85,7 @@ namespace Oqtane.Controllers
// DELETE api/<controller>/5
[HttpDelete("{id}")]
[Authorize(Roles = Constants.RegisteredRole)]
[Authorize(Roles = RoleNames.Registered)]
public void Delete(int id)
{
Notification notification = _notifications.GetNotification(id);

View File

@ -15,7 +15,7 @@ using Oqtane.Shared;
namespace Oqtane.Controllers
{
[Route("{alias}/api/[controller]")]
[Route(ControllerRoutes.Default)]
public class PackageController : Controller
{
private readonly IWebHostEnvironment _environment;
@ -27,15 +27,14 @@ namespace Oqtane.Controllers
// GET: api/<controller>?tag=x
[HttpGet]
[Authorize(Roles = Constants.HostRole)]
[Authorize(Roles = RoleNames.Host)]
public async Task<IEnumerable<Package>> Get(string tag)
{
List<Package> packages = new List<Package>();
using (var httpClient = new HttpClient())
{
CancellationToken token;
var searchResult = await GetJson<SearchResult>(httpClient, "https://azuresearch-usnc.nuget.org/query?q=tags:oqtane", token);
var searchResult = await GetJson<SearchResult>(httpClient, "https://azuresearch-usnc.nuget.org/query?q=tags:oqtane");
foreach(Data data in searchResult.Data)
{
if (data.Tags.Contains(tag))
@ -56,14 +55,13 @@ namespace Oqtane.Controllers
}
[HttpPost]
[Authorize(Roles = Constants.HostRole)]
[Authorize(Roles = RoleNames.Host)]
public async Task Post(string packageid, string version, string folder)
{
using (var httpClient = new HttpClient())
{
CancellationToken token;
folder = Path.Combine(_environment.WebRootPath, folder);
var response = await httpClient.GetAsync("https://www.nuget.org/api/v2/package/" + packageid.ToLower() + "/" + version, token).ConfigureAwait(false);
var response = await httpClient.GetAsync("https://www.nuget.org/api/v2/package/" + packageid.ToLower() + "/" + version).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
string filename = packageid + "." + version + ".nupkg";
using (var fileStream = new FileStream(Path.Combine(folder, filename), FileMode.Create, FileAccess.Write, FileShare.None))
@ -73,10 +71,10 @@ namespace Oqtane.Controllers
}
}
private async Task<T> GetJson<T>(HttpClient httpClient, string url, CancellationToken token)
private async Task<T> GetJson<T>(HttpClient httpClient, string url)
{
Uri uri = new Uri(url);
var response = await httpClient.GetAsync(uri, token).ConfigureAwait(false);
var response = await httpClient.GetAsync(uri).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
var stream = await response.Content.ReadAsStreamAsync();
using (var streamReader = new StreamReader(stream))

View File

@ -13,7 +13,7 @@ using Oqtane.Repository;
namespace Oqtane.Controllers
{
[Route("{alias}/api/[controller]")]
[Route(ControllerRoutes.Default)]
public class PageController : Controller
{
private readonly IPageRepository _pages;
@ -102,7 +102,7 @@ namespace Oqtane.Controllers
// POST api/<controller>
[HttpPost]
[Authorize(Roles = Constants.RegisteredRole)]
[Authorize(Roles = RoleNames.Registered)]
public Page Post([FromBody] Page page)
{
if (ModelState.IsValid)
@ -115,7 +115,7 @@ namespace Oqtane.Controllers
else
{
permissions = new List<Permission> {
new Permission(PermissionNames.Edit, Constants.AdminRole, true)
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
}.EncodePermissions();
}
@ -125,7 +125,7 @@ namespace Oqtane.Controllers
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.Site, page.SiteId);
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Page Added {Page}", page);
if (!page.EditMode)
if (!page.Path.StartsWith("admin/"))
{
var modules = _modules.GetModules(page.SiteId).Where(item => item.AllPages).ToList();
foreach (Module module in modules)
@ -147,7 +147,7 @@ namespace Oqtane.Controllers
// POST api/<controller>/5?userid=x
[HttpPost("{id}")]
[Authorize(Roles = Constants.RegisteredRole)]
[Authorize(Roles = RoleNames.Registered)]
public Page Post(int id, string userid)
{
Page page = null;
@ -163,7 +163,6 @@ namespace Oqtane.Controllers
page.Order = 0;
page.IsNavigation = false;
page.Url = "";
page.EditMode = false;
page.ThemeType = parent.ThemeType;
page.LayoutType = parent.LayoutType;
page.DefaultContainerType = parent.DefaultContainerType;
@ -214,7 +213,7 @@ namespace Oqtane.Controllers
// PUT api/<controller>/5
[HttpPut("{id}")]
[Authorize(Roles = Constants.RegisteredRole)]
[Authorize(Roles = RoleNames.Registered)]
public Page Put(int id, [FromBody] Page page)
{
if (ModelState.IsValid && _userPermissions.IsAuthorized(User, EntityNames.Page, page.PageId, PermissionNames.Edit))
@ -234,7 +233,7 @@ namespace Oqtane.Controllers
// PUT api/<controller>/?siteid=x&pageid=y&parentid=z
[HttpPut]
[Authorize(Roles = Constants.RegisteredRole)]
[Authorize(Roles = RoleNames.Registered)]
public void Put(int siteid, int pageid, int? parentid)
{
if (_userPermissions.IsAuthorized(User, EntityNames.Page, pageid, PermissionNames.Edit))
@ -262,7 +261,7 @@ namespace Oqtane.Controllers
// DELETE api/<controller>/5
[HttpDelete("{id}")]
[Authorize(Roles = Constants.RegisteredRole)]
[Authorize(Roles = RoleNames.Registered)]
public void Delete(int id)
{
Page page = _pages.GetPage(id);

View File

@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using Oqtane.Models;
@ -11,7 +11,7 @@ using Oqtane.Security;
namespace Oqtane.Controllers
{
[Route("{alias}/api/[controller]")]
[Route(ControllerRoutes.Default)]
public class PageModuleController : Controller
{
private readonly IPageModuleRepository _pageModules;
@ -65,13 +65,13 @@ namespace Oqtane.Controllers
// POST api/<controller>
[HttpPost]
[Authorize(Roles = Constants.RegisteredRole)]
[Authorize(Roles = RoleNames.Registered)]
public PageModule Post([FromBody] PageModule pageModule)
{
if (ModelState.IsValid && _userPermissions.IsAuthorized(User, EntityNames.Page, pageModule.PageId, PermissionNames.Edit))
{
pageModule = _pageModules.AddPageModule(pageModule);
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.Page, pageModule.PageId);
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.Site, _tenants.GetAlias().SiteId);
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Page Module Added {PageModule}", pageModule);
}
else
@ -85,13 +85,13 @@ namespace Oqtane.Controllers
// PUT api/<controller>/5
[HttpPut("{id}")]
[Authorize(Roles = Constants.RegisteredRole)]
[Authorize(Roles = RoleNames.Registered)]
public PageModule Put(int id, [FromBody] PageModule pageModule)
{
if (ModelState.IsValid && _userPermissions.IsAuthorized(User, EntityNames.Module, pageModule.ModuleId, PermissionNames.Edit))
{
pageModule = _pageModules.UpdatePageModule(pageModule);
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.Page, pageModule.PageId);
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.Site, _tenants.GetAlias().SiteId);
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Page Module Updated {PageModule}", pageModule);
}
else
@ -105,7 +105,7 @@ namespace Oqtane.Controllers
// PUT api/<controller>/?pageid=x&pane=y
[HttpPut]
[Authorize(Roles = Constants.RegisteredRole)]
[Authorize(Roles = RoleNames.Registered)]
public void Put(int pageid, string pane)
{
if (_userPermissions.IsAuthorized(User, EntityNames.Page, pageid, PermissionNames.Edit))
@ -121,7 +121,7 @@ namespace Oqtane.Controllers
}
order += 2;
}
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.Page, pageid);
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.Site, _tenants.GetAlias().SiteId);
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Page Module Order Updated {PageId} {Pane}", pageid, pane);
}
else
@ -133,14 +133,14 @@ namespace Oqtane.Controllers
// DELETE api/<controller>/5
[HttpDelete("{id}")]
[Authorize(Roles = Constants.RegisteredRole)]
[Authorize(Roles = RoleNames.Registered)]
public void Delete(int id)
{
PageModule pagemodule = _pageModules.GetPageModule(id);
if (_userPermissions.IsAuthorized(User, EntityNames.Page, pagemodule.PageId, PermissionNames.Edit))
{
_pageModules.DeletePageModule(id);
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.Page, pagemodule.PageId);
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.Site, _tenants.GetAlias().SiteId);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Page Module Deleted {PageModuleId}", id);
}
else

View File

@ -9,7 +9,7 @@ using Oqtane.Repository;
namespace Oqtane.Controllers
{
[Route("{alias}/api/[controller]")]
[Route(ControllerRoutes.Default)]
public class ProfileController : Controller
{
private readonly IProfileRepository _profiles;
@ -37,7 +37,7 @@ namespace Oqtane.Controllers
// POST api/<controller>
[HttpPost]
[Authorize(Roles = Constants.AdminRole)]
[Authorize(Roles = RoleNames.Admin)]
public Profile Post([FromBody] Profile profile)
{
if (ModelState.IsValid)
@ -50,7 +50,7 @@ namespace Oqtane.Controllers
// PUT api/<controller>/5
[HttpPut("{id}")]
[Authorize(Roles = Constants.AdminRole)]
[Authorize(Roles = RoleNames.Admin)]
public Profile Put(int id, [FromBody] Profile profile)
{
if (ModelState.IsValid)
@ -63,7 +63,7 @@ namespace Oqtane.Controllers
// DELETE api/<controller>/5
[HttpDelete("{id}")]
[Authorize(Roles = Constants.AdminRole)]
[Authorize(Roles = RoleNames.Admin)]
public void Delete(int id)
{
_profiles.DeleteProfile(id);

View File

@ -9,7 +9,7 @@ using Oqtane.Repository;
namespace Oqtane.Controllers
{
[Route("{alias}/api/[controller]")]
[Route(ControllerRoutes.Default)]
public class RoleController : Controller
{
private readonly IRoleRepository _roles;
@ -23,7 +23,7 @@ namespace Oqtane.Controllers
// GET: api/<controller>?siteid=x
[HttpGet]
[Authorize(Roles = Constants.RegisteredRole)]
[Authorize(Roles = RoleNames.Registered)]
public IEnumerable<Role> Get(string siteid)
{
return _roles.GetRoles(int.Parse(siteid));
@ -31,7 +31,7 @@ namespace Oqtane.Controllers
// GET api/<controller>/5
[HttpGet("{id}")]
[Authorize(Roles = Constants.RegisteredRole)]
[Authorize(Roles = RoleNames.Registered)]
public Role Get(int id)
{
return _roles.GetRole(id);
@ -39,7 +39,7 @@ namespace Oqtane.Controllers
// POST api/<controller>
[HttpPost]
[Authorize(Roles = Constants.AdminRole)]
[Authorize(Roles = RoleNames.Admin)]
public Role Post([FromBody] Role role)
{
if (ModelState.IsValid)
@ -52,7 +52,7 @@ namespace Oqtane.Controllers
// PUT api/<controller>/5
[HttpPut("{id}")]
[Authorize(Roles = Constants.AdminRole)]
[Authorize(Roles = RoleNames.Admin)]
public Role Put(int id, [FromBody] Role role)
{
if (ModelState.IsValid)
@ -65,7 +65,7 @@ namespace Oqtane.Controllers
// DELETE api/<controller>/5
[HttpDelete("{id}")]
[Authorize(Roles = Constants.AdminRole)]
[Authorize(Roles = RoleNames.Admin)]
public void Delete(int id)
{
_roles.DeleteRole(id);

View File

@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using Oqtane.Models;
using Oqtane.Shared;
@ -10,19 +10,23 @@ using Oqtane.Repository;
namespace Oqtane.Controllers
{
[Route("{alias}/api/[controller]")]
[Route(ControllerRoutes.Default)]
public class SettingController : Controller
{
private readonly ISettingRepository _settings;
private readonly IPageModuleRepository _pageModules;
private readonly IUserPermissions _userPermissions;
private readonly ITenantResolver _tenants;
private readonly ISyncManager _syncManager;
private readonly ILogManager _logger;
public SettingController(ISettingRepository settings, IPageModuleRepository pageModules, IUserPermissions userPermissions, ILogManager logger)
public SettingController(ISettingRepository settings, IPageModuleRepository pageModules, IUserPermissions userPermissions, ITenantResolver tenants, ISyncManager syncManager, ILogManager logger)
{
_settings = settings;
_pageModules = pageModules;
_userPermissions = userPermissions;
_tenants = tenants;
_syncManager = syncManager;
_logger = logger;
}
@ -67,6 +71,10 @@ namespace Oqtane.Controllers
if (ModelState.IsValid && IsAuthorized(setting.EntityName, setting.EntityId, PermissionNames.Edit))
{
setting = _settings.AddSetting(setting);
if (setting.EntityName == EntityNames.Module)
{
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.Site, _tenants.GetAlias().SiteId);
}
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Setting Added {Setting}", setting);
}
else
@ -85,6 +93,10 @@ namespace Oqtane.Controllers
if (ModelState.IsValid && IsAuthorized(setting.EntityName, setting.EntityId, PermissionNames.Edit))
{
setting = _settings.UpdateSetting(setting);
if (setting.EntityName == EntityNames.Module)
{
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.Site, _tenants.GetAlias().SiteId);
}
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Setting Updated {Setting}", setting);
}
else
@ -104,6 +116,10 @@ namespace Oqtane.Controllers
if (IsAuthorized(setting.EntityName, setting.EntityId, PermissionNames.Edit))
{
_settings.DeleteSetting(id);
if (setting.EntityName == EntityNames.Module)
{
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.Site, _tenants.GetAlias().SiteId);
}
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Setting Deleted {Setting}", setting);
}
else
@ -124,10 +140,10 @@ namespace Oqtane.Controllers
switch (entityName)
{
case EntityNames.Tenant:
authorized = User.IsInRole(Constants.HostRole);
authorized = User.IsInRole(RoleNames.Host);
break;
case EntityNames.Site:
authorized = User.IsInRole(Constants.AdminRole);
authorized = User.IsInRole(RoleNames.Admin);
break;
case EntityNames.Page:
case EntityNames.Module:
@ -138,7 +154,7 @@ namespace Oqtane.Controllers
authorized = true;
if (permissionName == PermissionNames.Edit)
{
authorized = User.IsInRole(Constants.AdminRole) || (_userPermissions.GetUser(User).UserId == entityId);
authorized = User.IsInRole(RoleNames.Admin) || (_userPermissions.GetUser(User).UserId == entityId);
}
break;
}

View File

@ -10,7 +10,7 @@ using Oqtane.Repository;
namespace Oqtane.Controllers
{
[Route("{alias}/api/[controller]")]
[Route(ControllerRoutes.Default)]
public class SiteController : Controller
{
private readonly ISiteRepository _sites;
@ -28,7 +28,7 @@ namespace Oqtane.Controllers
// GET: api/<controller>
[HttpGet]
[Authorize(Roles = Constants.HostRole)]
[Authorize(Roles = RoleNames.Host)]
public IEnumerable<Site> Get()
{
return _sites.GetSites();
@ -57,7 +57,7 @@ namespace Oqtane.Controllers
}
else
{
authorized = User.IsInRole(Constants.HostRole);
authorized = User.IsInRole(RoleNames.Host);
}
if (authorized)
{
@ -70,7 +70,7 @@ namespace Oqtane.Controllers
// PUT api/<controller>/5
[HttpPut("{id}")]
[Authorize(Roles = Constants.AdminRole)]
[Authorize(Roles = RoleNames.Admin)]
public Site Put(int id, [FromBody] Site site)
{
if (ModelState.IsValid)
@ -84,7 +84,7 @@ namespace Oqtane.Controllers
// DELETE api/<controller>/5
[HttpDelete("{id}")]
[Authorize(Roles = Constants.HostRole)]
[Authorize(Roles = RoleNames.Host)]
public void Delete(int id)
{
_sites.DeleteSite(id);

View File

@ -7,7 +7,7 @@ using Oqtane.Shared;
namespace Oqtane.Controllers
{
[Route("{alias}/api/[controller]")]
[Route(ControllerRoutes.Default)]
public class SiteTemplateController : Controller
{
private readonly ISiteTemplateRepository _siteTemplates;
@ -19,7 +19,7 @@ namespace Oqtane.Controllers
// GET: api/<controller>
[HttpGet]
[Authorize(Roles = Constants.HostRole)]
[Authorize(Roles = RoleNames.Host)]
public IEnumerable<SiteTemplate> Get()
{
return _siteTemplates.GetSiteTemplates();

View File

@ -14,7 +14,7 @@ using System;
namespace Oqtane.Controllers
{
[Route("{alias}/api/[controller]")]
[Route(ControllerRoutes.Default)]
public class SqlController : Controller
{
private readonly ITenantRepository _tenants;
@ -30,7 +30,7 @@ namespace Oqtane.Controllers
// POST: api/<controller>
[HttpPost]
[Authorize(Roles = Constants.HostRole)]
[Authorize(Roles = RoleNames.Host)]
public SqlQuery Post([FromBody] SqlQuery sqlquery)
{
var results = new List<Dictionary<string, string>>();

View File

@ -7,7 +7,7 @@ using Microsoft.AspNetCore.Hosting;
namespace Oqtane.Controllers
{
[Route("{alias}/api/[controller]")]
[Route(ControllerRoutes.Default)]
public class SystemController : Controller
{
private readonly IWebHostEnvironment _environment;
@ -19,7 +19,7 @@ namespace Oqtane.Controllers
// GET: api/<controller>
[HttpGet]
[Authorize(Roles = Constants.HostRole)]
[Authorize(Roles = RoleNames.Host)]
public Dictionary<string, string> Get()
{
Dictionary<string, string> systeminfo = new Dictionary<string, string>();

View File

@ -9,7 +9,7 @@ using Oqtane.Repository;
namespace Oqtane.Controllers
{
[Route("{alias}/api/[controller]")]
[Route(ControllerRoutes.Default)]
public class TenantController : Controller
{
private readonly ITenantRepository _tenants;
@ -23,7 +23,7 @@ namespace Oqtane.Controllers
// GET: api/<controller>
[HttpGet]
[Authorize(Roles = Constants.AdminRole)]
[Authorize(Roles = RoleNames.Admin)]
public IEnumerable<Tenant> Get()
{
return _tenants.GetTenants();
@ -31,7 +31,7 @@ namespace Oqtane.Controllers
// GET api/<controller>/5
[HttpGet("{id}")]
[Authorize(Roles = Constants.AdminRole)]
[Authorize(Roles = RoleNames.Admin)]
public Tenant Get(int id)
{
return _tenants.GetTenant(id);
@ -39,7 +39,7 @@ namespace Oqtane.Controllers
// POST api/<controller>
[HttpPost]
[Authorize(Roles = Constants.HostRole)]
[Authorize(Roles = RoleNames.Host)]
public Tenant Post([FromBody] Tenant tenant)
{
if (ModelState.IsValid)
@ -52,7 +52,7 @@ namespace Oqtane.Controllers
// PUT api/<controller>/5
[HttpPut("{id}")]
[Authorize(Roles = Constants.HostRole)]
[Authorize(Roles = RoleNames.Host)]
public Tenant Put(int id, [FromBody] Tenant tenant)
{
if (ModelState.IsValid)
@ -65,7 +65,7 @@ namespace Oqtane.Controllers
// DELETE api/<controller>/5
[HttpDelete("{id}")]
[Authorize(Roles = Constants.HostRole)]
[Authorize(Roles = RoleNames.Host)]
public void Delete(int id)
{
_tenants.DeleteTenant(id);

View File

@ -10,12 +10,13 @@ using Microsoft.AspNetCore.Hosting;
using Oqtane.Enums;
using Oqtane.Infrastructure;
using Oqtane.Repository;
using System.Text.Json;
// ReSharper disable StringIndexOfIsCultureSpecific.1
namespace Oqtane.Controllers
{
[Route("{alias}/api/[controller]")]
[Route(ControllerRoutes.Default)]
public class ThemeController : Controller
{
private readonly IThemeRepository _themes;
@ -33,63 +34,55 @@ namespace Oqtane.Controllers
// GET: api/<controller>
[HttpGet]
[Authorize(Roles = Constants.RegisteredRole)]
[Authorize(Roles = RoleNames.Registered)]
public IEnumerable<Theme> Get()
{
return _themes.GetThemes();
}
[HttpGet("install")]
[Authorize(Roles = Constants.HostRole)]
[Authorize(Roles = RoleNames.Host)]
public void InstallThemes()
{
_installationManager.InstallPackages("Themes", true);
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Themes Installed");
_installationManager.InstallPackages("Themes", true);
}
// DELETE api/<controller>/xxx
[HttpDelete("{themename}")]
[Authorize(Roles = Constants.HostRole)]
[Authorize(Roles = RoleNames.Host)]
public void Delete(string themename)
{
List<Theme> themes = _themes.GetThemes().ToList();
Theme theme = themes.Where(item => item.ThemeName == themename).FirstOrDefault();
if (theme != null && Utilities.GetAssemblyName(theme.ThemeName) != "Oqtane.Client")
{
// use assets.json to clean up file resources
string assetfilepath = Path.Combine(_environment.WebRootPath, "Themes", Utilities.GetTypeName(theme.ThemeName), "assets.json");
if (System.IO.File.Exists(assetfilepath))
{
List<string> assets = JsonSerializer.Deserialize<List<string>>(System.IO.File.ReadAllText(assetfilepath));
foreach (string asset in assets)
{
if (System.IO.File.Exists(asset))
{
System.IO.File.Delete(asset);
}
}
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Theme Assets Removed For {ThemeName}", theme.ThemeName);
}
// clean up theme static resource folder
string folder = Path.Combine(_environment.WebRootPath, "Themes" , Utilities.GetTypeName(theme.ThemeName));
if (Directory.Exists(folder))
{
Directory.Delete(folder, true);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Theme Static Resources Removed For {ThemeName}", theme.ThemeName);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Theme Resource Folder Removed For {ThemeName}", theme.ThemeName);
}
// remove theme assembly from /bin
string binfolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
System.IO.File.Delete(Path.Combine(binfolder, Utilities.GetAssemblyName(theme.ThemeName) + ".dll"));
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Theme Assembly {Filename} Removed For {ThemeName}", Utilities.GetAssemblyName(theme.ThemeName) + ".dll", themename);
_installationManager.RestartApplication();
}
}
// GET api/<controller>/load/assembyname
[HttpGet("load/{assemblyname}")]
public IActionResult Load(string assemblyname)
{
if (Path.GetExtension(assemblyname).ToLower() == ".dll")
{
string binfolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
byte[] file = System.IO.File.ReadAllBytes(Path.Combine(binfolder, assemblyname));
return File(file, "application/octet-stream", assemblyname);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Read, "User Not Authorized To Download Assembly {Assembly}", assemblyname);
HttpContext.Response.StatusCode = 401;
return null;
}
}
}
}

View File

@ -9,14 +9,16 @@ using System.Linq;
using System.Security.Claims;
using Oqtane.Shared;
using System;
using System.IO;
using System.Net;
using Oqtane.Enums;
using Oqtane.Infrastructure;
using Oqtane.Repository;
using Oqtane.Extensions;
namespace Oqtane.Controllers
{
[Route("{alias}/api/[controller]")]
[Route(ControllerRoutes.Default)]
public class UserController : Controller
{
private readonly IUserRepository _users;
@ -75,7 +77,7 @@ namespace Oqtane.Controllers
private User Filter(User user)
{
if (user != null && !User.IsInRole(Constants.AdminRole) && User.Identity.Name?.ToLower() != user.Username.ToLower())
if (user != null && !User.IsInRole(RoleNames.Admin) && User.Identity.Name?.ToLower() != user.Username.ToLower())
{
user.DisplayName = "";
user.Email = "";
@ -116,7 +118,7 @@ namespace Oqtane.Controllers
bool verified;
bool allowregistration;
if (user.Username == Constants.HostUser || User.IsInRole(Constants.AdminRole))
if (user.Username == UserNames.Host || User.IsInRole(RoleNames.Admin))
{
verified = true;
allowregistration = true;
@ -162,9 +164,9 @@ namespace Oqtane.Controllers
}
// assign to host role if this is the host user ( initial installation )
if (user.Username == Constants.HostUser)
if (user.Username == UserNames.Host)
{
int hostroleid = _roles.GetRoles(user.SiteId, true).Where(item => item.Name == Constants.HostRole).FirstOrDefault().RoleId;
int hostroleid = _roles.GetRoles(user.SiteId, true).Where(item => item.Name == RoleNames.Host).FirstOrDefault().RoleId;
UserRole userrole = new UserRole();
userrole.UserId = newUser.UserId;
userrole.RoleId = hostroleid;
@ -174,7 +176,7 @@ namespace Oqtane.Controllers
}
// add folder for user
Folder folder = _folders.GetFolder(user.SiteId, Utilities.PathCombine("Users","\\"));
Folder folder = _folders.GetFolder(user.SiteId, Utilities.PathCombine("Users",Path.DirectorySeparatorChar.ToString()));
if (folder != null)
{
_folders.AddFolder(new Folder
@ -182,11 +184,15 @@ namespace Oqtane.Controllers
SiteId = folder.SiteId,
ParentId = folder.FolderId,
Name = "My Folder",
Path = Utilities.PathCombine(folder.Path, newUser.UserId.ToString(),"\\"),
Path = Utilities.PathCombine(folder.Path, newUser.UserId.ToString(),Path.DirectorySeparatorChar.ToString()),
Order = 1,
IsSystem = true,
Permissions = "[{\"PermissionName\":\"Browse\",\"Permissions\":\"[" + newUser.UserId.ToString() + "]\"},{\"PermissionName\":\"View\",\"Permissions\":\"All Users\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"[" +
newUser.UserId.ToString() + "]\"}]"
Permissions = new List<Permission>
{
new Permission(PermissionNames.Browse, newUser.UserId, true),
new Permission(PermissionNames.View, RoleNames.Everyone, true),
new Permission(PermissionNames.Edit, newUser.UserId, true)
}.EncodePermissions()
});
}
}
@ -200,7 +206,7 @@ namespace Oqtane.Controllers
}
}
if (newUser != null && user.Username != Constants.HostUser)
if (newUser != null && user.Username != UserNames.Host)
{
// add auto assigned roles to user for site
List<Role> roles = _roles.GetRoles(user.SiteId).Where(item => item.IsAutoAssigned).ToList();
@ -236,7 +242,7 @@ namespace Oqtane.Controllers
{
if (ModelState.IsValid)
{
if (User.IsInRole(Constants.AdminRole) || User.Identity.Name == user.Username)
if (User.IsInRole(RoleNames.Admin) || User.Identity.Name == user.Username)
{
if (user.Password != "")
{
@ -264,7 +270,7 @@ namespace Oqtane.Controllers
// DELETE api/<controller>/5?siteid=x
[HttpDelete("{id}")]
[Authorize(Roles = Constants.AdminRole)]
[Authorize(Roles = RoleNames.Admin)]
public async Task Delete(int id)
{
IdentityUser identityuser = await _identityUserManager.FindByNameAsync(_users.GetUser(id).Username);
@ -454,9 +460,9 @@ namespace Oqtane.Controllers
foreach (UserRole userrole in userroles)
{
roles += userrole.Role.Name + ";";
if (userrole.Role.Name == Constants.HostRole && userroles.Where(item => item.Role.Name == Constants.AdminRole).FirstOrDefault() == null)
if (userrole.Role.Name == RoleNames.Host && userroles.Where(item => item.Role.Name == RoleNames.Admin).FirstOrDefault() == null)
{
roles += Constants.AdminRole + ";";
roles += RoleNames.Admin + ";";
}
}
if (roles != "") roles = ";" + roles;

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