diff --git a/Oqtane.Client/Modules/Admin/Modules/Settings.razor b/Oqtane.Client/Modules/Admin/Modules/Settings.razor
index a949271f..b7cf4f4c 100644
--- a/Oqtane.Client/Modules/Admin/Modules/Settings.razor
+++ b/Oqtane.Client/Modules/Admin/Modules/Settings.razor
@@ -97,6 +97,23 @@
+
+
}
@@ -144,6 +161,8 @@
private string _pane;
private string _containerType;
private string _allPages = "false";
+ private string _header = "";
+ private string _footer = "";
private string _permissionNames = "";
private List _permissions = null;
private string _pageId;
@@ -167,37 +186,47 @@
protected override async Task OnInitializedAsync()
{
SetModuleTitle(Localizer["ModuleSettings.Title"]);
-
- _title = ModuleState.Title;
_moduleSettingsTitle = Localizer["ModuleSettings.Heading"];
- _pane = ModuleState.Pane;
+
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, PageState.Page.ThemeType);
- _containerType = ModuleState.ContainerType;
- _allPages = ModuleState.AllPages.ToString();
- _permissions = ModuleState.PermissionList;
- _pageId = ModuleState.PageId.ToString();
- createdby = ModuleState.CreatedBy;
- createdon = ModuleState.CreatedOn;
- modifiedby = ModuleState.ModifiedBy;
- modifiedon = ModuleState.ModifiedOn;
- _effectivedate = Utilities.UtcAsLocalDate(ModuleState.EffectiveDate);
- _expirydate = Utilities.UtcAsLocalDate(ModuleState.ExpiryDate);
_pages = await PageService.GetPagesAsync(PageState.Site.SiteId);
- if (ModuleState.ModuleDefinition != null)
- {
- _module = ModuleState.ModuleDefinition.Name;
- _permissionNames = ModuleState.ModuleDefinition?.PermissionNames;
+ var pagemodule = await PageModuleService.GetPageModuleAsync(ModuleState.PageModuleId);
- if (!string.IsNullOrEmpty(ModuleState.ModuleDefinition.SettingsType))
+ _pageId = pagemodule.PageId.ToString();
+ _title = pagemodule.Title;
+ _pane = pagemodule.Pane;
+ _containerType = pagemodule.ContainerType;
+ if (string.IsNullOrEmpty(_containerType))
+ {
+ _containerType = (!string.IsNullOrEmpty(PageState.Page.DefaultContainerType)) ? PageState.Page.DefaultContainerType : PageState.Site.DefaultContainerType;
+ }
+ _header = pagemodule.Header;
+ _footer = pagemodule.Footer;
+ _effectivedate = Utilities.UtcAsLocalDate(pagemodule.EffectiveDate);
+ _expirydate = Utilities.UtcAsLocalDate(pagemodule.ExpiryDate);
+
+ _allPages = pagemodule.Module.AllPages.ToString();
+ createdby = pagemodule.Module.CreatedBy;
+ createdon = pagemodule.Module.CreatedOn;
+ modifiedby = pagemodule.Module.ModifiedBy;
+ modifiedon = pagemodule.Module.ModifiedOn;
+ _permissions = pagemodule.Module.PermissionList;
+
+ if (pagemodule.Module.ModuleDefinition != null)
+ {
+ _module = pagemodule.Module.ModuleDefinition.Name;
+ _permissionNames = pagemodule.Module.ModuleDefinition?.PermissionNames;
+
+ if (!string.IsNullOrEmpty(pagemodule.Module.ModuleDefinition.SettingsType))
{
// module settings type explicitly declared in IModule interface
- _moduleSettingsType = Type.GetType(ModuleState.ModuleDefinition.SettingsType);
+ _moduleSettingsType = Type.GetType(pagemodule.Module.ModuleDefinition.SettingsType);
}
else
{
// legacy support - module settings type determined by convention ( ie. existence of a "Settings.razor" component in module )
- _moduleSettingsType = Type.GetType(ModuleState.ModuleDefinition.ControlTypeTemplate.Replace(Constants.ActionToken, PageState.Action), false, true);
+ _moduleSettingsType = Type.GetType(pagemodule.Module.ModuleDefinition.ControlTypeTemplate.Replace(Constants.ActionToken, PageState.Action), false, true);
}
if (_moduleSettingsType != null)
{
@@ -218,7 +247,7 @@
}
else
{
- AddModuleMessage(string.Format(Localizer["Error.Module.Load"], ModuleState.ModuleDefinitionName), MessageType.Error);
+ AddModuleMessage(string.Format(Localizer["Error.Module.Load"], pagemodule.Module.ModuleDefinitionName), MessageType.Error);
}
var theme = PageState.Site.Themes.FirstOrDefault(item => item.Containers.Any(themecontrol => themecontrol.TypeName.Equals(_containerType)));
@@ -270,10 +299,12 @@
{
pagemodule.ContainerType = string.Empty;
}
+ pagemodule.Header = _header;
+ pagemodule.Footer = _footer;
await PageModuleService.UpdatePageModuleAsync(pagemodule);
await PageModuleService.UpdatePageModuleOrderAsync(pagemodule.PageId, pagemodule.Pane);
- var module = ModuleState;
+ var module = await ModuleService.GetModuleAsync(ModuleState.ModuleId);
module.AllPages = bool.Parse(_allPages);
module.PageModuleId = ModuleState.PageModuleId;
module.PermissionList = _permissionGrid.GetPermissionList();
diff --git a/Oqtane.Client/Resources/Modules/Admin/Modules/Settings.resx b/Oqtane.Client/Resources/Modules/Admin/Modules/Settings.resx
index a91aa36e..4f8f895c 100644
--- a/Oqtane.Client/Resources/Modules/Admin/Modules/Settings.resx
+++ b/Oqtane.Client/Resources/Modules/Admin/Modules/Settings.resx
@@ -189,4 +189,19 @@
Module Settings
+
+ Header:
+
+
+ Optionally provide content to be injected above the module instance
+
+
+ Footer:
+
+
+ Optionally provide content to be injected below the module instance
+
+
+ Content
+
\ No newline at end of file
diff --git a/Oqtane.Client/UI/ModuleInstance.razor b/Oqtane.Client/UI/ModuleInstance.razor
index 1bd5296f..e8353a82 100644
--- a/Oqtane.Client/UI/ModuleInstance.razor
+++ b/Oqtane.Client/UI/ModuleInstance.razor
@@ -1,6 +1,7 @@
@namespace Oqtane.UI
@inject SiteState SiteState
+@((MarkupString)ModuleState.Header)
@if (_comment != null)
{
@((MarkupString)_comment)
@@ -13,6 +14,7 @@
}
}
+@((MarkupString)ModuleState.Footer)
@code {
[CascadingParameter]
@@ -23,6 +25,8 @@
private bool _prerender;
private string _comment;
+ private string _header;
+ private string _footer;
protected override void OnParametersSet()
{
@@ -39,11 +43,16 @@
}
_comment += " -->";
+ _header = ModuleState.Header;
+ _footer = ModuleState.Footer;
+
if (PageState.RenderMode == RenderModes.Static && ModuleState.RenderMode == RenderModes.Interactive)
{
// trim PageState to mitigate page bloat caused by Blazor serializing/encrypting state when crossing render mode boundaries
// please note that this performance optimization results in the PageState.Pages property not being available for use in Interactive components
PageState.Site.Pages = new List();
+ ModuleState.Header = string.Empty;
+ ModuleState.Footer = string.Empty;
}
}
diff --git a/Oqtane.Server/Controllers/ModuleController.cs b/Oqtane.Server/Controllers/ModuleController.cs
index a7c09fcb..02f6f8c6 100644
--- a/Oqtane.Server/Controllers/ModuleController.cs
+++ b/Oqtane.Server/Controllers/ModuleController.cs
@@ -76,6 +76,8 @@ namespace Oqtane.Controllers
module.ContainerType = pagemodule.ContainerType;
module.EffectiveDate = pagemodule.EffectiveDate;
module.ExpiryDate = pagemodule.ExpiryDate;
+ module.Header = pagemodule.Header;
+ module.Footer = pagemodule.Footer;
module.ModuleDefinition = _moduleDefinitions.FilterModuleDefinition(moduledefinitions.Find(item => item.ModuleDefinitionName == module.ModuleDefinitionName));
diff --git a/Oqtane.Server/Controllers/PageController.cs b/Oqtane.Server/Controllers/PageController.cs
index ee2ff20c..6086bd0f 100644
--- a/Oqtane.Server/Controllers/PageController.cs
+++ b/Oqtane.Server/Controllers/PageController.cs
@@ -246,6 +246,10 @@ namespace Oqtane.Controllers
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);
}
diff --git a/Oqtane.Server/Migrations/Tenant/06010302_AddModuleHeaderFooter.cs b/Oqtane.Server/Migrations/Tenant/06010302_AddModuleHeaderFooter.cs
new file mode 100644
index 00000000..39e01287
--- /dev/null
+++ b/Oqtane.Server/Migrations/Tenant/06010302_AddModuleHeaderFooter.cs
@@ -0,0 +1,29 @@
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Oqtane.Databases.Interfaces;
+using Oqtane.Migrations.EntityBuilders;
+using Oqtane.Repository;
+
+namespace Oqtane.Migrations.Tenant
+{
+ [DbContext(typeof(TenantDBContext))]
+ [Migration("Tenant.06.01.03.02")]
+ public class AddModuleHeaderFooter : MultiDatabaseMigration
+ {
+ public AddModuleHeaderFooter(IDatabase database) : base(database)
+ {
+ }
+
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ var pageModuleEntityBuilder = new PageModuleEntityBuilder(migrationBuilder, ActiveDatabase);
+ pageModuleEntityBuilder.AddMaxStringColumn("Header", true);
+ pageModuleEntityBuilder.AddMaxStringColumn("Footer", true);
+ }
+
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ // not implemented
+ }
+ }
+}
diff --git a/Oqtane.Server/Repository/SiteRepository.cs b/Oqtane.Server/Repository/SiteRepository.cs
index 10de3576..48620ead 100644
--- a/Oqtane.Server/Repository/SiteRepository.cs
+++ b/Oqtane.Server/Repository/SiteRepository.cs
@@ -442,6 +442,8 @@ namespace Oqtane.Repository
pageModule.Pane = (string.IsNullOrEmpty(pageTemplateModule.Pane)) ? PaneNames.Default : pageTemplateModule.Pane;
pageModule.Order = (pageTemplateModule.Order == 0) ? 1 : pageTemplateModule.Order;
pageModule.ContainerType = pageTemplateModule.ContainerType;
+ pageModule.Header = pageTemplateModule.Header;
+ pageModule.Footer = pageTemplateModule.Footer;
pageModule.IsDeleted = pageTemplateModule.IsDeleted;
pageModule.Module.PermissionList = new List();
foreach (var permission in pageTemplateModule.PermissionList)
diff --git a/Oqtane.Server/Services/SiteService.cs b/Oqtane.Server/Services/SiteService.cs
index cc0991d8..f316b766 100644
--- a/Oqtane.Server/Services/SiteService.cs
+++ b/Oqtane.Server/Services/SiteService.cs
@@ -285,6 +285,8 @@ namespace Oqtane.Services
ContainerType = pagemodule.ContainerType,
EffectiveDate = pagemodule.EffectiveDate,
ExpiryDate = pagemodule.ExpiryDate,
+ Header = pagemodule.Header,
+ Footer = pagemodule.Footer,
ModuleDefinition = _moduleDefinitions.FilterModuleDefinition(moduledefinitions.Find(item => item.ModuleDefinitionName == pagemodule.Module.ModuleDefinitionName)),
diff --git a/Oqtane.Shared/Models/Module.cs b/Oqtane.Shared/Models/Module.cs
index 55f31357..7775fcf5 100644
--- a/Oqtane.Shared/Models/Module.cs
+++ b/Oqtane.Shared/Models/Module.cs
@@ -113,6 +113,18 @@ namespace Oqtane.Models
[NotMapped]
public DateTime? ExpiryDate { get; set; }
+ ///
+ /// Header content to include at the top of a module instance in the UI
+ ///
+ [NotMapped]
+ public string Header { get; set; }
+
+ ///
+ /// Footer content to include below a module instance in the UI
+ ///
+ [NotMapped]
+ public string Footer { get; set; }
+
#endregion
#region SiteRouter properties
@@ -218,6 +230,8 @@ namespace Oqtane.Models
ContainerType = ContainerType,
EffectiveDate = EffectiveDate,
ExpiryDate = ExpiryDate,
+ Header = Header,
+ Footer = Footer,
CreatedBy = CreatedBy,
CreatedOn = CreatedOn,
ModifiedBy = ModifiedBy,
diff --git a/Oqtane.Shared/Models/PageModule.cs b/Oqtane.Shared/Models/PageModule.cs
index 0e7c812f..20985e93 100644
--- a/Oqtane.Shared/Models/PageModule.cs
+++ b/Oqtane.Shared/Models/PageModule.cs
@@ -41,14 +41,27 @@ namespace Oqtane.Models
/// Reference to a Razor Container which wraps this module instance.
///
public string ContainerType { get; set; }
+
///
/// Start of when this assignment is valid. See also
///
public DateTime? EffectiveDate { get; set; }
+
///
/// End of when this assignment is valid. See also
///
public DateTime? ExpiryDate { get; set; }
+
+ ///
+ /// Header content to include above the module instance in the UI
+ ///
+ public string Header { get; set; }
+
+ ///
+ /// Footer content to include below the module instance in the UI
+ ///
+ public string Footer { get; set; }
+
#region IDeletable Properties
public string DeletedBy { get; set; }
diff --git a/Oqtane.Shared/Models/SiteTemplate.cs b/Oqtane.Shared/Models/SiteTemplate.cs
index 348e1834..42cbffee 100644
--- a/Oqtane.Shared/Models/SiteTemplate.cs
+++ b/Oqtane.Shared/Models/SiteTemplate.cs
@@ -95,6 +95,8 @@ namespace Oqtane.Models
Pane = PaneNames.Default;
Order = 1;
ContainerType = "";
+ Header = "";
+ Footer = "";
IsDeleted = false;
PermissionList = new List()
{
@@ -110,6 +112,8 @@ namespace Oqtane.Models
public string Pane { get; set; }
public int Order { get; set; }
public string ContainerType { get; set; }
+ public string Header { get; set; }
+ public string Footer { get; set; }
public bool IsDeleted { get; set; }
public List PermissionList { get; set; }
public List Settings { get; set; }