diff --git a/Oqtane.Client/Modules/Admin/Pages/Edit.razor b/Oqtane.Client/Modules/Admin/Pages/Edit.razor index 029680ce..4fe412c5 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Edit.razor @@ -46,7 +46,7 @@
- +
- - -
+ @if (!_copy) + { + + +
    @Localizer["ModuleTitle"] @Localizer["ModuleDefinition"] -
- - - - @context.Title - @context.ModuleDefinition?.Name - -
-
- @if (_themeSettingsType != null) - { - - @_themeSettingsComponent -
- - +
+ + + + @context.Title + @context.ModuleDefinition?.Name + +
+ @if (_themeSettingsType != null) + { + + @_themeSettingsComponent +
+ + +
+ } + } } @@ -349,6 +353,7 @@ private List _containers = new List(); private List _pages; private int _pageId; + private bool _copy = false; private string _name; private string _currentparentid; private string _parentid = "-1"; @@ -394,6 +399,10 @@ { _pages = await PageService.GetPagesAsync(PageState.Site.SiteId); _pageId = Int32.Parse(PageState.QueryString["id"]); + if (PageState.QueryString.ContainsKey("copy")) + { + _copy = bool.Parse(PageState.QueryString["copy"]); + } _page = await PageService.GetPageAsync(_pageId); _icons = await SystemService.GetIconsAsync(); _iconresources = Utilities.GetFullTypeName(typeof(IconResources).AssemblyQualifiedName); @@ -413,7 +422,7 @@ _children = new List(); foreach (Page p in _pages.Where(item => (_parentid == "-1" && item.ParentId == null) || (item.ParentId == int.Parse(_parentid, CultureInfo.InvariantCulture)))) { - if (p.PageId != _pageId && UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.PermissionList)) + if ((p.PageId != _pageId || _copy) && UserSecurity.IsAuthorized(PageState.User, PermissionNames.View, p.PermissionList)) { _children.Add(p); } @@ -440,6 +449,12 @@ _expirydate = Utilities.UtcAsLocalDate(_page.ExpiryDate); _ispersonalizable = _page.IsPersonalizable.ToString(); + if (_copy) + { + _insert = ">"; + _childid = _page.PageId; + } + // appearance _title = _page.Title; _themetype = _page.ThemeType; @@ -470,6 +485,19 @@ // permissions _permissions = _page.PermissionList; _updatemodulepermissions = "True"; + if (_copy) + { + _permissions = _page.PermissionList.Select(item => new Permission + { + SiteId = item.SiteId, + EntityName = item.EntityName, + EntityId = -1, + PermissionName = item.PermissionName, + RoleName = item.RoleName, + UserId = item.UserId, + IsAuthorized = item.IsAuthorized, + }).ToList(); + } // page modules var modules = await ModuleService.GetModulesAsync(PageState.Site.SiteId); @@ -484,6 +512,13 @@ _deletedon = _page.DeletedOn; ThemeSettings(); + + if (_copy) + { + _name = ""; + _path = ""; + } + _initialized = true; } else @@ -554,7 +589,7 @@ builder.OpenComponent(0, _themeSettingsType); builder.AddAttribute(1, "RenderModeBoundary", RenderModeBoundary); builder.AddComponentReferenceCapture(2, inst => { _themeSettings = Convert.ChangeType(inst, _themeSettingsType); }); - + builder.CloseComponent(); }; } @@ -581,6 +616,13 @@ { string currentPath = _page.Path; + if (_copy) + { + _page = new Page(); + _page.SiteId = PageState.Site.SiteId; + currentPath = ""; + } + _page.Name = _name; if (_parentid == "-1") @@ -696,8 +738,19 @@ _page.UpdateModulePermissions = bool.Parse(_updatemodulepermissions); } - // update page - _page = await PageService.UpdatePageAsync(_page); + if (_copy) + { + // create page + _page = await PageService.AddPageAsync(_page); + await PageService.CopyPageAsync(_pageId, _page.PageId, bool.Parse(_updatemodulepermissions)); + await logger.LogInformation("Page Added {Page}", _page); + } + else + { + // update page + _page = await PageService.UpdatePageAsync(_page); + await logger.LogInformation("Page Saved {Page}", _page); + } // update page order await PageService.UpdatePageOrderAsync(_page.SiteId, _page.PageId, _page.ParentId); @@ -710,7 +763,6 @@ await PageService.UpdatePageOrderAsync(_page.SiteId, _page.PageId, int.Parse(_currentparentid)); } - await logger.LogInformation("Page Saved {Page}", _page); if (!string.IsNullOrEmpty(PageState.ReturnUrl)) { NavigationManager.NavigateTo(PageState.ReturnUrl, true); // redirect to page being edited and reload diff --git a/Oqtane.Client/Resources/Themes/Controls/ControlPanelInteractive.resx b/Oqtane.Client/Resources/Themes/Controls/ControlPanelInteractive.resx index 5247824e..1ef062f4 100644 --- a/Oqtane.Client/Resources/Themes/Controls/ControlPanelInteractive.resx +++ b/Oqtane.Client/Resources/Themes/Controls/ControlPanelInteractive.resx @@ -201,4 +201,7 @@ Synchronize Site + + Copy Page + \ No newline at end of file diff --git a/Oqtane.Client/Services/PageService.cs b/Oqtane.Client/Services/PageService.cs index e3f940c6..4beefcd0 100644 --- a/Oqtane.Client/Services/PageService.cs +++ b/Oqtane.Client/Services/PageService.cs @@ -71,6 +71,15 @@ namespace Oqtane.Services /// /// Task DeletePageAsync(int pageId); + + /// + /// Copies the modules from one page to another + /// + /// + /// + /// + /// + Task CopyPageAsync(int fromPageId, int toPageId, bool usePagePermissions); } [PrivateApi("Don't show in the documentation, as everything should use the Interface")] @@ -129,5 +138,10 @@ namespace Oqtane.Services { await DeleteAsync($"{Apiurl}/{pageId}"); } + + public async Task CopyPageAsync(int fromPageId, int toPageId, bool usePagePermissions) + { + await PostAsync($"{Apiurl}/{fromPageId}/{toPageId}/{usePagePermissions}"); + } } } diff --git a/Oqtane.Client/Themes/Controls/Theme/ControlPanelInteractive.razor b/Oqtane.Client/Themes/Controls/Theme/ControlPanelInteractive.razor index 028b33ec..377bde6b 100644 --- a/Oqtane.Client/Themes/Controls/Theme/ControlPanelInteractive.razor +++ b/Oqtane.Client/Themes/Controls/Theme/ControlPanelInteractive.razor @@ -59,18 +59,29 @@ -
-
- @if (UserSecurity.ContainsRole(PageState.Page.PermissionList, PermissionNames.View, RoleNames.Everyone)) - { - - } - else - { - - } + @if (PageState.Page.UserId == null) + { +
+
+ +
-
+ } + @if (!PageState.Page.Path.StartsWith("admin/")) + { +
+
+ @if (UserSecurity.ContainsRole(PageState.Page.PermissionList, PermissionNames.View, RoleNames.Everyone)) + { + + } + else + { + + } +
+
+ }
@if (_deleteConfirmation) @@ -496,7 +507,12 @@ case "Edit": // get page management moduleid moduleId = int.Parse(PageState.Site.Settings[Constants.PageManagementModule]); - NavigationManager.NavigateTo(Utilities.EditUrl(PageState.Alias.Path, "admin/pages", moduleId, location, $"id={PageState.Page.PageId}&returnurl={WebUtility.UrlEncode(PageState.Route.PathAndQuery)}")); + NavigationManager.NavigateTo(Utilities.EditUrl(PageState.Alias.Path, "admin/pages", moduleId, "Edit", $"id={PageState.Page.PageId}&returnurl={WebUtility.UrlEncode(PageState.Route.PathAndQuery)}")); + break; + case "Copy": + // get page management moduleid + moduleId = int.Parse(PageState.Site.Settings[Constants.PageManagementModule]); + NavigationManager.NavigateTo(Utilities.EditUrl(PageState.Alias.Path, "admin/pages", moduleId, "Edit", $"id={PageState.Page.PageId}©=true&returnurl={WebUtility.UrlEncode(PageState.Route.PathAndQuery)}")); break; } } diff --git a/Oqtane.Server/Controllers/PageController.cs b/Oqtane.Server/Controllers/PageController.cs index 69bed0bc..3a367bb2 100644 --- a/Oqtane.Server/Controllers/PageController.cs +++ b/Oqtane.Server/Controllers/PageController.cs @@ -1,16 +1,15 @@ using System.Collections.Generic; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Authorization; -using Oqtane.Models; -using Oqtane.Shared; using System.Linq; -using Oqtane.Security; using System.Net; +using System.Security; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; using Oqtane.Enums; using Oqtane.Infrastructure; +using Oqtane.Models; using Oqtane.Repository; -using System.Xml.Linq; -using Microsoft.AspNetCore.Diagnostics; +using Oqtane.Security; +using Oqtane.Shared; namespace Oqtane.Controllers { @@ -498,6 +497,79 @@ namespace Oqtane.Controllers HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; } } - } + // POST api//5/6 + [HttpPost("{fromPageId}/{toPageId}/{usePagePermissions}")] + [Authorize(Roles = RoleNames.Registered)] + public void Post(int fromPageId, int toPageId, bool usePagePermissions) + { + var fromPage = _pages.GetPage(fromPageId); + if (fromPage != null && fromPage.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.View, fromPage.PermissionList)) + { + var toPage = _pages.GetPage(toPageId); + if (toPage != null && toPage.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.View, toPage.PermissionList)) + { + // copy modules + List pageModules = _pageModules.GetPageModules(fromPage.SiteId).ToList(); + foreach (PageModule pm in pageModules.Where(item => item.PageId == fromPage.PageId && !item.Module.AllPages && !item.IsDeleted)) + { + Module module = new Module(); + module.SiteId = fromPage.SiteId; + module.PageId = toPageId; + module.ModuleDefinitionName = pm.Module.ModuleDefinitionName; + module.AllPages = false; + if (usePagePermissions) + { + module.PermissionList = toPage.PermissionList; + } + else + { + module.PermissionList = pm.Module.PermissionList; + } + module.PermissionList = module.PermissionList.Select(item => new Permission + { + SiteId = item.SiteId, + EntityName = EntityNames.Module, + EntityId = -1, + PermissionName = item.PermissionName, + RoleName = item.RoleName, + UserId = item.UserId, + IsAuthorized = item.IsAuthorized, + }).ToList(); + module = _modules.AddModule(module); + + string content = _modules.ExportModule(pm.ModuleId); + if (content != "") + { + _modules.ImportModule(module.ModuleId, content); + } + + PageModule pageModule = new PageModule(); + pageModule.PageId = toPageId; + pageModule.ModuleId = module.ModuleId; + pageModule.Title = pm.Title; + pageModule.Pane = pm.Pane; + pageModule.Order = pm.Order; + pageModule.ContainerType = pm.ContainerType; + pageModule.EffectiveDate = pm.EffectiveDate; + pageModule.ExpiryDate = pm.ExpiryDate; + pageModule.Header = pm.Header; + pageModule.Footer = pm.Footer; + + _pageModules.AddPageModule(pageModule); + } + + _syncManager.AddSyncEvent(_alias, EntityNames.Site, fromPage.SiteId, SyncEventActions.Refresh); + } + else + { + HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; + } + } + else + { + HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; + } + } + } } diff --git a/Oqtane.Server/Infrastructure/Jobs/SynchronizationJob.cs b/Oqtane.Server/Infrastructure/Jobs/SynchronizationJob.cs index f699fe20..2b574833 100644 --- a/Oqtane.Server/Infrastructure/Jobs/SynchronizationJob.cs +++ b/Oqtane.Server/Infrastructure/Jobs/SynchronizationJob.cs @@ -17,7 +17,7 @@ namespace Oqtane.Infrastructure // synchronization only supports sites in the same tenant (database) // module title is used as a key to identify module instances on a page - // modules must implement ISynchronizable interface + // modules must implement ISynchronizable interface for content synchronization // change detection does not support deleted items as key values will usually be different due to localization // define settings that should not be synchronized (should be extensible in the future)