diff --git a/DNN Platform/DotNetNuke.Abstractions/Pages/IPageService.cs b/DNN Platform/DotNetNuke.Abstractions/Pages/IPageService.cs
new file mode 100644
index 00000000000..8d19654b8d7
--- /dev/null
+++ b/DNN Platform/DotNetNuke.Abstractions/Pages/IPageService.cs
@@ -0,0 +1,109 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information
+
+namespace DotNetNuke.Abstractions.Pages
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Text;
+
+ ///
+ /// Provides services for managing page content with priority-based storage.
+ ///
+ public interface IPageService
+ {
+ ///
+ /// Set the Page Title.
+ ///
+ /// The title value to set.
+ /// The priority of this title (lower number = higher priority).
+ void SetTitle(string value, int priority = PagePriority.Default);
+
+ ///
+ /// Set the Page Description.
+ ///
+ /// The description value to set.
+ /// The priority of this description (lower number = higher priority).
+ void SetDescription(string value, int priority = PagePriority.Default);
+
+ ///
+ /// Set the Page Keywords.
+ ///
+ /// The keywords value to set.
+ /// The priority of these keywords (lower number = higher priority).
+ void SetKeyWords(string value, int priority = PagePriority.Default);
+
+ ///
+ /// Set the Page Canonical Link URL.
+ ///
+ /// The canonical link URL value to set.
+ /// The priority of this canonical link URL (lower number = higher priority).
+ void SetCanonicalLinkUrl(string value, int priority = PagePriority.Default);
+
+ ///
+ /// Add a tag to the header of the page.
+ ///
+ /// Priority item containing the tag and priority information.
+ void AddToHead(PageTag tagItem);
+
+ ///
+ /// Add a standard meta header tag.
+ ///
+ /// Priority meta item containing the meta tag information and priority.
+ void AddMeta(PageMeta metaItem);
+
+ ///
+ /// Add a message to be displayed on the page.
+ ///
+ /// Priority message item containing the message information and priority.
+ void AddMessage(PageMessage messageItem);
+
+ ///
+ /// Gets the current title value with highest priority.
+ ///
+ /// The title value or null if not set.
+ string GetTitle();
+
+ ///
+ /// Gets the current description value with highest priority.
+ ///
+ /// The description value or null if not set.
+ string GetDescription();
+
+ ///
+ /// Gets the current keywords value with highest priority.
+ ///
+ /// The keywords value or null if not set.
+ string GetKeyWords();
+
+ ///
+ /// Gets the canonical link URL.
+ ///
+ /// The canonical link URL or null if not set.
+ string GetCanonicalLinkUrl();
+
+ ///
+ /// Gets all head tags ordered by priority (lowest priority first).
+ ///
+ /// List of head tags ordered by priority.
+ IEnumerable GetHeadTags();
+
+ ///
+ /// Gets all meta tags ordered by priority (lowest priority first).
+ ///
+ /// List of meta tags ordered by priority.
+ IEnumerable GetMetaTags();
+
+ ///
+ /// Gets all messages ordered by priority (lowest priority first).
+ ///
+ /// List of messages ordered by priority.
+ IEnumerable GetMessages();
+
+ ///
+ /// Clears all stored page data. Useful for testing or resetting state.
+ ///
+ void Clear();
+ }
+}
diff --git a/DNN Platform/DotNetNuke.Abstractions/Pages/PageMessage.cs b/DNN Platform/DotNetNuke.Abstractions/Pages/PageMessage.cs
new file mode 100644
index 00000000000..75d2b5add53
--- /dev/null
+++ b/DNN Platform/DotNetNuke.Abstractions/Pages/PageMessage.cs
@@ -0,0 +1,100 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information
+
+namespace DotNetNuke.Abstractions.Pages
+{
+ ///
+ /// Represents a message with priority information for display on a page.
+ /// Messages can include success notifications, warnings, errors, or informational content.
+ ///
+ ///
+ /// This class is used to store messages that will be displayed to users on a page.
+ /// Priority determines the order in which messages are displayed, with lower numbers having higher priority.
+ /// Common priority values are defined in .
+ ///
+ ///
+ ///
+ /// // Create a success message with high priority
+ /// var successMessage = new PriorityMessage(
+ /// "Operation Successful",
+ /// "The data has been saved successfully.",
+ /// MessageType.Success,
+ /// "/images/success-icon.png",
+ /// PagePriority.Site);
+ ///
+ /// // Create an error message with default priority
+ /// var errorMessage = new PriorityMessage(
+ /// "Validation Error",
+ /// "Please check the required fields.",
+ /// MessageType.Error,
+ /// "",
+ /// PagePriority.Default);
+ ///
+ ///
+ public class PageMessage
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The heading text for the message.
+ /// The message text content. Cannot be null or empty.
+ /// The type/severity of the message.
+ /// The optional icon source URL for the message. Use empty string if no icon is needed.
+ /// The priority of the message. Use values from for consistency.
+ public PageMessage(string heading, string message, PageMessageType messageType, string iconSrc, int priority)
+ {
+ this.Heading = heading;
+ this.Message = message;
+ this.MessageType = messageType;
+ this.IconSrc = iconSrc;
+ this.Priority = priority;
+ }
+
+ ///
+ /// Gets or sets the heading text for the message.
+ ///
+ ///
+ /// A string containing the heading text that will be displayed prominently.
+ /// This should be a concise summary of the message.
+ ///
+ public string Heading { get; set; }
+
+ ///
+ /// Gets or sets the message text content.
+ ///
+ ///
+ /// A string containing the detailed message content that will be displayed to the user.
+ /// This can contain HTML markup for formatting.
+ ///
+ public string Message { get; set; }
+
+ ///
+ /// Gets or sets the type/severity of the message.
+ ///
+ ///
+ /// A value indicating the severity or type of the message.
+ /// This affects how the message is styled and displayed to the user.
+ ///
+ public PageMessageType MessageType { get; set; }
+
+ ///
+ /// Gets or sets the optional icon source URL for the message.
+ ///
+ ///
+ /// A string containing the URL or path to an icon image, or an empty string if no icon is needed.
+ /// The icon will be displayed alongside the message to provide visual context.
+ ///
+ public string IconSrc { get; set; }
+
+ ///
+ /// Gets or sets the priority of the message (lower number = higher priority).
+ ///
+ ///
+ /// An integer representing the display priority of the message.
+ /// Messages with lower priority numbers will be displayed before those with higher numbers.
+ /// Use constants from for consistent priority values.
+ ///
+ public int Priority { get; set; }
+ }
+}
diff --git a/DNN Platform/DotNetNuke.Abstractions/Pages/PageMessageType.cs b/DNN Platform/DotNetNuke.Abstractions/Pages/PageMessageType.cs
new file mode 100644
index 00000000000..6bcb3206a64
--- /dev/null
+++ b/DNN Platform/DotNetNuke.Abstractions/Pages/PageMessageType.cs
@@ -0,0 +1,73 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information
+
+namespace DotNetNuke.Abstractions.Pages
+{
+ ///
+ /// Defines the types and severity levels for page messages.
+ /// Message types determine how messages are styled and presented to users.
+ ///
+ ///
+ /// These enumeration values are used to categorize messages by their purpose and severity.
+ /// The UI layer typically uses these values to apply appropriate styling, colors, and icons
+ /// to provide visual context to users about the nature of the message.
+ ///
+ ///
+ ///
+ /// // Success message for completed operations
+ /// var successMsg = new PageMessage("Operation Complete", "Data saved successfully", MessageType.Success, "", PagePriority.Default);
+ ///
+ /// // Warning message for potential issues
+ /// var warningMsg = new PageMessage("Warning", "This action cannot be undone", MessageType.Warning, "", PagePriority.Default);
+ ///
+ /// // Error message for failed operations
+ /// var errorMsg = new PageMessage("Error", "Failed to save data", MessageType.Error, "", PagePriority.Default);
+ ///
+ /// // Informational message for general notifications
+ /// var infoMsg = new PageMessage("Notice", "System maintenance scheduled", MessageType.Info, "", PagePriority.Default);
+ ///
+ ///
+ public enum PageMessageType
+ {
+ ///
+ /// Success message type.
+ /// Used for messages indicating successful completion of operations.
+ /// Typically displayed with green styling and success icons.
+ ///
+ ///
+ /// Use for: Data saved, user created, operation completed, etc.
+ ///
+ Success = 0,
+
+ ///
+ /// Warning message type.
+ /// Used for messages indicating potential issues or important notices that require user attention.
+ /// Typically displayed with yellow/orange styling and warning icons.
+ ///
+ ///
+ /// Use for: Validation warnings, deprecation notices, cautionary information, etc.
+ ///
+ Warning = 1,
+
+ ///
+ /// Error message type.
+ /// Used for messages indicating failed operations or critical issues.
+ /// Typically displayed with red styling and error icons.
+ ///
+ ///
+ /// Use for: Validation errors, operation failures, system errors, etc.
+ ///
+ Error = 2,
+
+ ///
+ /// Informational message type.
+ /// Used for general notifications and informational content.
+ /// Typically displayed with blue styling and info icons.
+ ///
+ ///
+ /// Use for: General notifications, tips, system status updates, etc.
+ ///
+ Info = 3,
+ }
+}
diff --git a/DNN Platform/DotNetNuke.Abstractions/Pages/PageMeta.cs b/DNN Platform/DotNetNuke.Abstractions/Pages/PageMeta.cs
new file mode 100644
index 00000000000..d68fa97c475
--- /dev/null
+++ b/DNN Platform/DotNetNuke.Abstractions/Pages/PageMeta.cs
@@ -0,0 +1,84 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information
+
+namespace DotNetNuke.Abstractions.Pages
+{
+ ///
+ /// Represents a meta tag with priority information for inclusion in the HTML head section.
+ /// Meta tags provide metadata about the HTML document and are used by search engines and browsers.
+ ///
+ ///
+ /// This class is used to store meta tag information that will be rendered in the HTML head section.
+ /// Priority determines the order in which meta tags are rendered, with lower numbers having higher priority.
+ /// Common priority values are defined in .
+ ///
+ ///
+ ///
+ /// // Create a description meta tag with page priority
+ /// var descriptionMeta = new PriorityMeta(
+ /// "description",
+ /// "This is the page description for SEO purposes.",
+ /// PagePriority.Page);
+ ///
+ /// // Create a keywords meta tag with default priority
+ /// var keywordsMeta = new PriorityMeta(
+ /// "keywords",
+ /// "DNN, CMS, content management",
+ /// PagePriority.Default);
+ ///
+ /// // Create a viewport meta tag with site priority
+ /// var viewportMeta = new PriorityMeta(
+ /// "viewport",
+ /// "width=device-width, initial-scale=1.0",
+ /// PagePriority.Site);
+ ///
+ ///
+ public class PageMeta
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The name attribute of the meta tag. Cannot be null or empty.
+ /// The content attribute of the meta tag. Cannot be null.
+ /// The priority of the meta tag (lower number = higher priority). Use values from for consistency.
+ /// Thrown when name or content is null.
+ /// Thrown when name is empty.
+ public PageMeta(string name, string content, int priority)
+ {
+ this.Name = name;
+ this.Content = content;
+ this.Priority = priority;
+ }
+
+ ///
+ /// Gets or sets the name attribute of the meta tag.
+ ///
+ ///
+ /// A string containing the name attribute value for the meta tag.
+ /// Common values include "description", "keywords", "author", "viewport", etc.
+ /// This will be rendered as: <meta name="[Name]" content="[Content]" />.
+ ///
+ public string Name { get; set; }
+
+ ///
+ /// Gets or sets the content attribute of the meta tag.
+ ///
+ ///
+ /// A string containing the content attribute value for the meta tag.
+ /// This contains the actual metadata information associated with the name attribute.
+ /// The content should be appropriate for the specified name attribute.
+ ///
+ public string Content { get; set; }
+
+ ///
+ /// Gets or sets the priority of the meta tag (lower number = higher priority).
+ ///
+ ///
+ /// An integer representing the rendering priority of the meta tag in the HTML head section.
+ /// Meta tags with lower priority numbers will be rendered before those with higher numbers.
+ /// Use constants from for consistent priority values.
+ ///
+ public int Priority { get; set; }
+ }
+}
diff --git a/DNN Platform/DotNetNuke.Abstractions/Pages/PagePriority.cs b/DNN Platform/DotNetNuke.Abstractions/Pages/PagePriority.cs
new file mode 100644
index 00000000000..8638ad3ec18
--- /dev/null
+++ b/DNN Platform/DotNetNuke.Abstractions/Pages/PagePriority.cs
@@ -0,0 +1,76 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information
+
+namespace DotNetNuke.Abstractions.Pages
+{
+ ///
+ /// Defines standard priority values for page content ordering.
+ /// Lower numbers indicate higher priority and will be processed/rendered first.
+ ///
+ ///
+ /// These constants provide a standardized way to assign priorities to page content such as
+ /// scripts, styles, meta tags, and messages. Using these predefined values ensures consistent
+ /// ordering across the application and makes the code more maintainable.
+ ///
+ /// Priority processing order:
+ /// 1. Site-level content (priority 10)
+ /// 2. Page-level content (priority 20)
+ /// 3. Default content (priority 100)
+ /// 4. Module-level content (priority 200).
+ ///
+ public static class PagePriority
+ {
+ ///
+ /// Site-level priority (value: 10).
+ /// Used for framework site-wide content
+ /// that should be loaded before any other content.
+ ///
+ public const int Site = 10;
+
+ ///
+ /// Page-level priority (value: 20).
+ /// Used for page-specific content that should be loaded after site-level content
+ /// but before default and module content.
+ ///
+ ///
+ ///
+ /// // Page-specific JavaScript
+ /// pageService.AddToHead(new PriorityItem("<script src=\"page-analytics.js\"></script>", PagePriority.Page));
+ ///
+ /// // Page-specific meta description
+ /// pageService.AddMeta(new PriorityMeta("description", "Page-specific description", PagePriority.Page));
+ ///
+ ///
+ public const int Page = 20;
+
+ ///
+ /// Default priority (value: 100).
+ /// Used as the standard priority when no specific priority is needed.
+ /// Most content should use this priority unless there's a specific ordering requirement.
+ ///
+ ///
+ ///
+ /// // Standard content without specific ordering requirements
+ /// pageService.AddMessage(new PriorityMessage("Info", "General information", MessageType.Info, "", PagePriority.Default));
+ ///
+ /// // Regular meta tags
+ /// pageService.AddMeta(new PriorityMeta("author", "John Doe", PagePriority.Default));
+ ///
+ ///
+ public const int Default = 100;
+
+ ///
+ /// Module-level priority (value: 200).
+ /// Used for module-specific content that should be loaded after all other content.
+ /// This ensures that module content doesn't interfere with core functionality.
+ ///
+ ///
+ ///
+ /// // Module-specific success message
+ /// pageService.AddMessage(new PriorityMessage("Success", "Module operation completed", MessageType.Success, "", PagePriority.Module));
+ ///
+ ///
+ public const int Module = 200;
+ }
+}
diff --git a/DNN Platform/DotNetNuke.Abstractions/Pages/PageTag.cs b/DNN Platform/DotNetNuke.Abstractions/Pages/PageTag.cs
new file mode 100644
index 00000000000..99837a6e096
--- /dev/null
+++ b/DNN Platform/DotNetNuke.Abstractions/Pages/PageTag.cs
@@ -0,0 +1,73 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information
+
+namespace DotNetNuke.Abstractions.Pages
+{
+ ///
+ /// Represents a string item with priority information for inclusion in page content.
+ /// This class is used for HTML tags, scripts, styles, and other string-based content that needs priority-based ordering.
+ ///
+ ///
+ /// This class is commonly used for HTML tags that need to be injected into different sections of a page
+ /// (head, body top, body end) with specific priority ordering. Priority determines the order in which
+ /// items are rendered.
+ /// Common priority values are defined in .
+ ///
+ ///
+ ///
+ /// // Create a script tag with high priority for the head section
+ /// var scriptTag = new PriorityItem(
+ /// "<script src=\"/js/framework.js\"></script>",
+ /// PagePriority.Site);
+ ///
+ /// // Create a style tag with module priority
+ /// var styleTag = new PriorityItem(
+ /// "<link rel=\"stylesheet\" href=\"/css/module.css\" />",
+ /// PagePriority.Module);
+ ///
+ /// // Create a meta tag with page priority
+ /// var metaTag = new PriorityItem(
+ /// "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />",
+ /// PagePriority.Page);
+ ///
+ ///
+ public class PageTag
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The string value of the item. Cannot be null.
+ /// The priority of the item . Use values from for consistency.
+ /// Thrown when value is null.
+ public PageTag(string value, int priority)
+ {
+ this.Value = value;
+ this.Priority = priority;
+ }
+
+ ///
+ /// Gets or sets the string value of the item.
+ ///
+ ///
+ /// A string containing the content to be rendered. This is typically HTML content such as
+ /// script tags, link tags, meta tags, or other HTML elements. The content should be properly
+ /// formatted HTML that is valid for the intended injection location.
+ ///
+ public string Value { get; set; }
+
+ ///
+ /// Gets or sets the priority of the item.
+ ///
+ ///
+ /// An integer representing the rendering priority of the item.
+ /// Items with lower priority numbers will be rendered before those with higher numbers.
+ /// Use constants from for consistent priority values:
+ /// - (10): Framework and site-level content
+ /// - (20): Page-specific content
+ /// - (100): Default priority for most content
+ /// - (200): Module-specific content.
+ ///
+ public int Priority { get; set; }
+ }
+}
diff --git a/DNN Platform/DotNetNuke.Abstractions/Pages/readme.md b/DNN Platform/DotNetNuke.Abstractions/Pages/readme.md
new file mode 100644
index 00000000000..8042ce7b144
--- /dev/null
+++ b/DNN Platform/DotNetNuke.Abstractions/Pages/readme.md
@@ -0,0 +1,100 @@
+### Enhancement: Priority-based IPageService for page metadata, head tags, and messages
+
+#### Summary
+Add a simple, priority-driven API for setting page title/description/keywords/canonical URL, injecting head tags and meta tags, and collecting user-visible messages. This formalizes page composition logic and decouples it from the rendering pipeline, supporting both the current WebForms pipeline and the new MVC pipeline.
+
+#### Motivation
+- **Unify page composition**: Provide a single abstraction for metadata, head elements, and messages across pipelines.
+- **Deterministic ordering**: Ensure consistent output using explicit priorities.
+- **Progress toward MVC/Core**: Keeps page state management out of WebForms, easing the hybrid transition.
+- **Testability**: A small, mockable surface that’s easy to unit test.
+
+#### Scope
+- Interfaces and models:
+ - `IPageService`
+ - `PageMessage`, `PageMessageType`
+ - `PageMeta`
+ - `PageTag`
+ - `PagePriority`
+
+
+#### Proposed API (already defined)
+- **Title/SEO**
+ - `void SetTitle(string value, int priority = PagePriority.Default)`
+ - `void SetDescription(string value, int priority = PagePriority.Default)`
+ - `void SetKeyWords(string value, int priority = PagePriority.Default)`
+ - `void SetCanonicalLinkUrl(string value, int priority = PagePriority.Default)`
+ - `string GetTitle()`, `string GetDescription()`, `string GetKeyWords()`, `string GetCanonicalLinkUrl()`
+- **Head content**
+ - `void AddToHead(PageTag tagItem)`
+ - `List GetHeadTags()`
+ - `void AddMeta(PageMeta metaItem)`
+ - `List GetMetaTags()`
+- **Messages**
+ - `void AddMessage(PageMessage messageItem)`
+ - `List GetMessages()`
+- **Maintenance**
+ - `void Clear()`
+- **Priority model**
+ - `PagePriority.Site = 10`, `PagePriority.Page = 20`, `PagePriority.Default = 100`, `PagePriority.Module = 200`
+- **Message model**
+ - `PageMessageType`: `Success`, `Warning`, `Error`, `Info`
+ - `PageMessage`: `Heading`, `Message`, `MessageType`, `IconSrc`, `Priority`
+- **Meta/head models**
+ - `PageMeta`: `Name`, `Content`, `Priority`
+ - `PageTag`: `Value`, `Priority`
+
+#### Example usage
+```csharp
+pageService.SetTitle("Products", PagePriority.Page);
+pageService.SetDescription("Browse our product catalog.", PagePriority.Page);
+pageService.SetCanonicalLinkUrl("https://www.mysite.com/products", PagePriority.Page);
+
+pageService.AddMeta(new PageMeta("viewport", "width=device-width, initial-scale=1.0", PagePriority.Site));
+pageService.AddToHead(new PageTag("", PagePriority.Page));
+
+pageService.AddMessage(new PageMessage(
+ "Saved", "Your product was updated.", PageMessageType.Success, "", PagePriority.Default));
+```
+
+#### Rendering contract (follow-up implementation)
+- The renderer (WebForms skin, MVC layout) must:
+ - Pick the highest-priority values for title/description/keywords/canonical link.
+ - Render `GetMetaTags()` and `GetHeadTags()` in ascending `Priority` order.
+ - Render `GetMessages()` in ascending `Priority` order, with styling determined by `PageMessageType`.
+
+
+
+
+
+#### Convenience extensions (`Library/Services/Pages/PageExtensions.cs`)
+These helpers provide simpler overloads for common operations:
+
+```csharp
+// Head content
+void IPageService.AddToHead(string tag, int priority = PagePriority.Default);
+void IPageService.AddMeta(string name, string content, int priority = PagePriority.Default);
+
+// Messages with explicit type
+void IPageService.AddMessage(string heading, string message, PageMessageType messageType, int priority = PagePriority.Default);
+void IPageService.AddMessage(string heading, string message, PageMessageType messageType, string iconSrc, int priority = PagePriority.Default);
+
+// Convenience by type
+void IPageService.AddSuccessMessage(string heading, string message, int priority = PagePriority.Default);
+void IPageService.AddErrorMessage(string heading, string message, int priority = PagePriority.Default);
+void IPageService.AddWarningMessage(string heading, string message, int priority = PagePriority.Default);
+void IPageService.AddInfoMessage(string heading, string message, int priority = PagePriority.Default);
+
+// Mapping helper for UI layer
+DotNetNuke.UI.Skins.Controls.ModuleMessage.ModuleMessageType PageMessageType.ToModuleMessageType();
+```
+
+Example:
+```csharp
+pageService.AddToHead("", PagePriority.Site);
+pageService.AddMeta("robots", "max-image-preview:large", PagePriority.Page);
+
+pageService.AddSuccessMessage("Saved", "Settings updated successfully.");
+pageService.AddWarningMessage("Heads up", "This feature is experimental.", PagePriority.Page);
+```
+
diff --git a/DNN Platform/HttpModules/Membership/MembershipModule.cs b/DNN Platform/HttpModules/Membership/MembershipModule.cs
index 13b581af4b9..1c1ccccb67e 100644
--- a/DNN Platform/HttpModules/Membership/MembershipModule.cs
+++ b/DNN Platform/HttpModules/Membership/MembershipModule.cs
@@ -5,12 +5,14 @@ namespace DotNetNuke.HttpModules.Membership
{
using System;
using System.Linq;
+ using System.Runtime.CompilerServices;
using System.Security.Principal;
using System.Threading;
using System.Web;
using System.Web.Security;
using DotNetNuke.Abstractions.Application;
+ using DotNetNuke.Abstractions.Pages;
using DotNetNuke.Common;
using DotNetNuke.Common.Utilities;
using DotNetNuke.Entities.Host;
@@ -66,10 +68,12 @@ public MembershipModule(IHostSettingsService hostSettingsService, IPortalControl
/// Called when unverified user skin initialize.
/// The sender.
/// The instance containing the event data.
+ [Obsolete("This method has been deprecated. No equivalent. Scheduled removal in v11.0.0.")]
public static void OnUnverifiedUserSkinInit(object sender, SkinEventArgs e)
{
+ var pageService = Globals.GetCurrentServiceProvider().GetRequiredService();
var strMessage = Localization.GetString("UnverifiedUser", Localization.SharedResourceFile, Thread.CurrentThread.CurrentCulture.Name);
- UI.Skins.Skin.AddPageMessage(e.Skin, string.Empty, strMessage, ModuleMessage.ModuleMessageType.YellowWarning);
+ pageService.AddMessage(new PageMessage(string.Empty, strMessage, PageMessageType.Warning, string.Empty, PagePriority.Site));
}
/// Authenticates the request.
diff --git a/DNN Platform/Library/DotNetNuke.Library.csproj b/DNN Platform/Library/DotNetNuke.Library.csproj
index 650546a4ae6..2a2598b5584 100644
--- a/DNN Platform/Library/DotNetNuke.Library.csproj
+++ b/DNN Platform/Library/DotNetNuke.Library.csproj
@@ -890,6 +890,8 @@
+
+
diff --git a/DNN Platform/Library/Framework/JavaScriptLibraries/JavaScript.cs b/DNN Platform/Library/Framework/JavaScriptLibraries/JavaScript.cs
index 76e541932a0..1d3b7d4088a 100644
--- a/DNN Platform/Library/Framework/JavaScriptLibraries/JavaScript.cs
+++ b/DNN Platform/Library/Framework/JavaScriptLibraries/JavaScript.cs
@@ -12,6 +12,7 @@ namespace DotNetNuke.Framework.JavaScriptLibraries
using DotNetNuke.Abstractions.Application;
using DotNetNuke.Abstractions.Logging;
+ using DotNetNuke.Abstractions.Pages;
using DotNetNuke.Abstractions.Portals;
using DotNetNuke.Common;
using DotNetNuke.Common.Utilities;
@@ -602,9 +603,11 @@ private static void LogCollision(IEventLogger eventLogger, IPortalSettings porta
UserController.Instance.GetCurrentUserInfo().UserID,
EventLogType.SCRIPT_COLLISION);
var strMessage = Localization.GetString("ScriptCollision", Localization.SharedResourceFile);
- if (HttpContextSource.Current?.Handler is Page page)
+
+ if (HttpContextSource.Current != null)
{
- Skin.AddPageMessage(page, string.Empty, strMessage, ModuleMessage.ModuleMessageType.YellowWarning);
+ var pageService = Globals.GetCurrentServiceProvider().GetRequiredService();
+ pageService.AddMessage(new PageMessage(string.Empty, strMessage, PageMessageType.Warning, string.Empty, PagePriority.Default));
}
}
diff --git a/DNN Platform/Library/Services/Pages/PageExtensions.cs b/DNN Platform/Library/Services/Pages/PageExtensions.cs
new file mode 100644
index 00000000000..3222468033f
--- /dev/null
+++ b/DNN Platform/Library/Services/Pages/PageExtensions.cs
@@ -0,0 +1,138 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information
+
+namespace DotNetNuke.Services.Pages
+{
+ using DotNetNuke.Abstractions.Pages;
+ using DotNetNuke.UI.Skins.Controls;
+
+ ///
+ /// Extension methods for that provide convenient overloads with simple parameters.
+ /// These methods create the appropriate priority objects internally for easier usage.
+ ///
+ public static class PageExtensions
+ {
+ ///
+ /// Adds a tag to the header of the page using simple parameters.
+ ///
+ /// The page service instance.
+ /// The HTML tag to add to the head section.
+ /// The priority of this tag. Defaults to .
+ public static void AddToHead(this IPageService pageService, string tag, int priority = PagePriority.Default)
+ {
+ var priorityItem = new PageTag(tag, priority);
+ pageService.AddToHead(priorityItem);
+ }
+
+ ///
+ /// Adds a meta tag using simple parameters.
+ ///
+ /// The page service instance.
+ /// The name attribute of the meta tag.
+ /// The content attribute of the meta tag.
+ /// The priority of this meta tag. Defaults to .
+ public static void AddMeta(this IPageService pageService, string name, string content, int priority = PagePriority.Default)
+ {
+ var priorityMeta = new PageMeta(name, content, priority);
+ pageService.AddMeta(priorityMeta);
+ }
+
+ ///
+ /// Adds a message using simple parameters with default message type.
+ ///
+ /// The page service instance.
+ /// The heading text for the message.
+ /// The message text content.
+ /// The priority of this message. Defaults to .
+ public static void AddMessage(this IPageService pageService, string heading, string message, int priority = PagePriority.Default)
+ {
+ var priorityMessage = new PageMessage(heading, message, PageMessageType.Info, string.Empty, priority);
+ pageService.AddMessage(priorityMessage);
+ }
+
+ ///
+ /// Adds a message using simple parameters with specified message type and icon.
+ ///
+ /// The page service instance.
+ /// The heading text for the message.
+ /// The message text content.
+ /// The type/severity of the message.
+ /// The optional icon source URL for the message.
+ /// The priority of this message. Defaults to .
+ public static void AddMessage(this IPageService pageService, string heading, string message, PageMessageType messageType, string iconSrc, int priority = PagePriority.Default)
+ {
+ var priorityMessage = new PageMessage(heading, message, messageType, iconSrc ?? string.Empty, priority);
+ pageService.AddMessage(priorityMessage);
+ }
+
+ ///
+ /// Adds a success message using simple parameters.
+ ///
+ /// The page service instance.
+ /// The heading text for the success message.
+ /// The success message text content.
+ /// The priority of this message. Defaults to .
+ public static void AddSuccessMessage(this IPageService pageService, string heading, string message, int priority = PagePriority.Default)
+ {
+ var priorityMessage = new PageMessage(heading, message, PageMessageType.Success, string.Empty, priority);
+ pageService.AddMessage(priorityMessage);
+ }
+
+ ///
+ /// Adds an error message using simple parameters.
+ ///
+ /// The page service instance.
+ /// The heading text for the error message.
+ /// The error message text content.
+ /// The priority of this message. Defaults to .
+ public static void AddErrorMessage(this IPageService pageService, string heading, string message, int priority = PagePriority.Default)
+ {
+ var priorityMessage = new PageMessage(heading, message, PageMessageType.Error, string.Empty, priority);
+ pageService.AddMessage(priorityMessage);
+ }
+
+ ///
+ /// Adds a warning message using simple parameters.
+ ///
+ /// The page service instance.
+ /// The heading text for the warning message.
+ /// The warning message text content.
+ /// The priority of this message. Defaults to .
+ public static void AddWarningMessage(this IPageService pageService, string heading, string message, int priority = PagePriority.Default)
+ {
+ var priorityMessage = new PageMessage(heading, message, PageMessageType.Warning, string.Empty, priority);
+ pageService.AddMessage(priorityMessage);
+ }
+
+ ///
+ /// Adds an informational message using simple parameters.
+ ///
+ /// The page service instance.
+ /// The heading text for the info message.
+ /// The info message text content.
+ /// The priority of this message. Defaults to .
+ public static void AddInfoMessage(this IPageService pageService, string heading, string message, int priority = PagePriority.Default)
+ {
+ var priorityMessage = new PageMessage(heading, message, PageMessageType.Info, string.Empty, priority);
+ pageService.AddMessage(priorityMessage);
+ }
+
+ ///
+ /// Converts a to a .
+ ///
+ /// The to convert.
+ /// The .
+ public static ModuleMessage.ModuleMessageType ToModuleMessageType(this PageMessageType priorityMessage)
+ {
+ return priorityMessage switch
+ {
+ PageMessageType.Success => ModuleMessage.ModuleMessageType.GreenSuccess,
+ PageMessageType.Warning => ModuleMessage.ModuleMessageType.YellowWarning,
+ PageMessageType.Error => ModuleMessage.ModuleMessageType.RedError,
+ PageMessageType.Info => ModuleMessage.ModuleMessageType.BlueInfo,
+ _ => ModuleMessage.ModuleMessageType.BlueInfo,
+ };
+ }
+ }
+}
diff --git a/DNN Platform/Library/Services/Pages/PageService.cs b/DNN Platform/Library/Services/Pages/PageService.cs
new file mode 100644
index 00000000000..ad83a146218
--- /dev/null
+++ b/DNN Platform/Library/Services/Pages/PageService.cs
@@ -0,0 +1,272 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information
+
+namespace DotNetNuke.Services.Pages
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+
+ using DotNetNuke.Abstractions.Pages;
+
+ ///
+ /// Provides services for managing page-level elements including meta tags, head tags, messages, and SEO properties.
+ /// This service supports priority-based management where higher priority values take precedence.
+ ///
+ ///
+ /// The PageService manages various page-level elements:
+ /// - Page metadata (title, description, keywords) with priority-based overrides
+ /// - Custom head tags for additional HTML head content
+ /// - Meta tags for SEO and page information
+ /// - Page messages for user notifications
+ /// Priority system: Higher priority values (e.g., 200) will override lower priority values (e.g., 100).
+ /// Default priority is 100 for most operations.
+ ///
+ public class PageService : IPageService
+ {
+ ///
+ /// Collection of head tags to be included in the HTML head section, ordered by priority.
+ ///
+ private readonly List headTags = new List();
+
+ ///
+ /// Collection of meta tags for SEO and page information, ordered by priority.
+ ///
+ private readonly List metaTags = new List();
+
+ ///
+ /// Collection of page messages for user notifications, ordered by priority.
+ ///
+ private readonly List messages = new List();
+
+ ///
+ /// The current page title with the highest priority.
+ ///
+ private PageTag title;
+
+ ///
+ /// The current page description with the highest priority.
+ ///
+ private PageTag description;
+
+ ///
+ /// The current page keywords with the highest priority.
+ ///
+ private PageTag keywords;
+
+ ///
+ /// The current page canonical link URL with the highest priority.
+ ///
+ private PageTag canonicalLinkUrl;
+
+ ///
+ /// Adds a message to the page's message collection.
+ ///
+ /// The message item to add to the page. Cannot be null.
+ /// Thrown when is null.
+ ///
+ /// Messages are used to display notifications, alerts, or informational content to users.
+ /// They are ordered by priority when retrieved, with lower priority values appearing first.
+ ///
+ public void AddMessage(PageMessage messageItem)
+ {
+ if (messageItem == null)
+ {
+ throw new ArgumentNullException(nameof(messageItem));
+ }
+
+ this.messages.Add(messageItem);
+ }
+
+ ///
+ /// Adds a meta tag to the page's meta tag collection.
+ ///
+ /// The meta tag item to add to the page. Cannot be null.
+ /// Thrown when is null.
+ ///
+ /// Meta tags provide metadata about the HTML document and are typically used for SEO,
+ /// social media sharing, and browser behavior. They are rendered in the HTML head section
+ /// and ordered by priority when retrieved.
+ ///
+ public void AddMeta(PageMeta metaItem)
+ {
+ if (metaItem == null)
+ {
+ throw new ArgumentNullException(nameof(metaItem));
+ }
+
+ this.metaTags.Add(metaItem);
+ }
+
+ ///
+ /// Adds a custom tag to the page's HTML head section.
+ ///
+ /// The tag item to add to the head section. Cannot be null.
+ /// Thrown when is null.
+ ///
+ /// Head tags allow you to include custom HTML elements in the page's head section,
+ /// such as custom CSS links, JavaScript files, or other HTML elements.
+ /// Tags are ordered by priority when retrieved, with lower priority values appearing first.
+ ///
+ public void AddToHead(PageTag tagItem)
+ {
+ if (tagItem == null)
+ {
+ throw new ArgumentNullException(nameof(tagItem));
+ }
+
+ this.headTags.Add(tagItem);
+ }
+
+ ///
+ /// Sets the page description with the specified priority.
+ ///
+ /// The description text for the page.
+ /// The priority level for this description. Higher values take precedence. Default is 100.
+ ///
+ /// The page description is typically used in meta tags for SEO purposes and may appear
+ /// in search engine results. Only the description with the highest priority will be used.
+ /// If multiple descriptions have the same priority, the last one set will be used.
+ ///
+ public void SetDescription(string value, int priority = 100)
+ {
+ this.SetHighestPriorityValue(ref this.description, value, priority);
+ }
+
+ ///
+ /// Sets the page keywords with the specified priority.
+ ///
+ /// The keywords for the page, typically comma-separated.
+ /// The priority level for these keywords. Higher values take precedence. Default is 100.
+ ///
+ /// Page keywords are used for SEO purposes and help categorize the page content.
+ /// Only the keywords with the highest priority will be used.
+ /// If multiple keyword sets have the same priority, the last one set will be used.
+ ///
+ public void SetKeyWords(string value, int priority = 100)
+ {
+ this.SetHighestPriorityValue(ref this.keywords, value, priority);
+ }
+
+ ///
+ /// Sets the page title with the specified priority.
+ ///
+ /// The title text for the page.
+ /// The priority level for this title. Higher values take precedence. Default is 100.
+ ///
+ /// The page title appears in the browser tab, search engine results, and when sharing on social media.
+ /// Only the title with the highest priority will be used.
+ /// If multiple titles have the same priority, the last one set will be used.
+ ///
+ public void SetTitle(string value, int priority = 100)
+ {
+ this.SetHighestPriorityValue(ref this.title, value, priority);
+ }
+
+ ///
+ /// Sets the page canonical link URL with the specified priority.
+ ///
+ /// The canonical link URL for the page.
+ /// The priority level for this canonical link URL. Higher values take precedence. Default is 100.
+ public void SetCanonicalLinkUrl(string value, int priority = 100)
+ {
+ this.SetHighestPriorityValue(ref this.canonicalLinkUrl, value, priority);
+ }
+
+ ///
+ /// Gets the current canonical link URL value with highest priority.
+ ///
+ /// The canonical link URL value or null if not set.
+ public string GetCanonicalLinkUrl()
+ {
+ return this.canonicalLinkUrl?.Value;
+ }
+
+ ///
+ /// Gets the current title value with highest priority.
+ ///
+ /// The title value or null if not set.
+ public string GetTitle()
+ {
+ return this.title?.Value;
+ }
+
+ ///
+ /// Gets the current description value with highest priority.
+ ///
+ /// The description value or null if not set.
+ public string GetDescription()
+ {
+ return this.description?.Value;
+ }
+
+ ///
+ /// Gets the current keywords value with highest priority.
+ ///
+ /// The keywords value or null if not set.
+ public string GetKeyWords()
+ {
+ return this.keywords?.Value;
+ }
+
+ ///
+ /// Gets all head tags ordered by priority (lowest priority first).
+ ///
+ /// List of head tags ordered by priority.
+ public IEnumerable GetHeadTags()
+ {
+ return this.headTags.OrderBy(x => x.Priority).ToList();
+ }
+
+ ///
+ /// Gets all meta tags ordered by priority (lowest priority first).
+ ///
+ /// List of meta tags ordered by priority.
+ public IEnumerable GetMetaTags()
+ {
+ return this.metaTags.OrderBy(x => x.Priority).ToList();
+ }
+
+ ///
+ /// Gets all messages ordered by priority (lowest priority first).
+ ///
+ /// List of messages ordered by priority.
+ public IEnumerable GetMessages()
+ {
+ return this.messages.OrderBy(x => x.Priority).ToList();
+ }
+
+ ///
+ /// Clears all stored page data. Useful for testing or resetting state.
+ ///
+ public void Clear()
+ {
+ this.title = null;
+ this.description = null;
+ this.keywords = null;
+ this.headTags.Clear();
+ this.metaTags.Clear();
+ this.messages.Clear();
+ }
+
+ ///
+ /// Sets the highest priority value for a PageTag field.
+ ///
+ /// Reference to the current PageTag field to potentially update.
+ /// The new value to set.
+ /// The priority of the new value.
+ ///
+ /// This method implements the priority-based override system. It only updates the current item
+ /// if the new priority is higher than the existing priority, or if no item is currently set.
+ /// This ensures that higher priority values always take precedence.
+ ///
+ private void SetHighestPriorityValue(ref PageTag currentItem, string value, int priority)
+ {
+ if (currentItem == null || priority > currentItem.Priority)
+ {
+ currentItem = new PageTag(value, priority);
+ }
+ }
+ }
+}
diff --git a/DNN Platform/Library/Startup.cs b/DNN Platform/Library/Startup.cs
index 415de11fe49..5cc33c47f8e 100644
--- a/DNN Platform/Library/Startup.cs
+++ b/DNN Platform/Library/Startup.cs
@@ -1,4 +1,4 @@
-// Licensed to the .NET Foundation under one or more agreements.
+// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information
namespace DotNetNuke
@@ -10,6 +10,7 @@ namespace DotNetNuke
using DotNetNuke.Abstractions.Application;
using DotNetNuke.Abstractions.Logging;
using DotNetNuke.Abstractions.Modules;
+ using DotNetNuke.Abstractions.Pages;
using DotNetNuke.Abstractions.Portals;
using DotNetNuke.Abstractions.Portals.Templates;
using DotNetNuke.Abstractions.Prompt;
@@ -47,6 +48,7 @@ namespace DotNetNuke
using DotNetNuke.Services.Log.EventLog;
using DotNetNuke.Services.Mail.OAuth;
using DotNetNuke.Services.Mobile;
+ using DotNetNuke.Services.Pages;
using DotNetNuke.Services.Personalization;
using DotNetNuke.Services.Search.Controllers;
using DotNetNuke.Services.Search.Internals;
@@ -90,6 +92,7 @@ public void ConfigureServices(IServiceCollection services)
services.AddScoped();
services.AddScoped();
+ services.AddScoped();
services.AddTransient();
services.AddTransient();
diff --git a/DNN Platform/Library/UI/Skins/Skin.cs b/DNN Platform/Library/UI/Skins/Skin.cs
index 052cb62226b..9bc18a06f00 100644
--- a/DNN Platform/Library/UI/Skins/Skin.cs
+++ b/DNN Platform/Library/UI/Skins/Skin.cs
@@ -214,7 +214,8 @@ public static void AddModuleMessage(Control control, string heading, string mess
/// The Message Heading.
/// The Message Text.
/// The Icon to display.
- public static void AddPageMessage(Page page, string heading, string message, string iconSrc)
+ [DnnDeprecated(10, 2, 0, "Please use IPageService.AddMessage")]
+ public static partial void AddPageMessage(Page page, string heading, string message, string iconSrc)
{
AddPageMessage(page, heading, message, ModuleMessage.ModuleMessageType.GreenSuccess, iconSrc);
}
@@ -224,7 +225,8 @@ public static void AddPageMessage(Page page, string heading, string message, str
/// The Message Heading.
/// The Message Text.
/// The Icon to display.
- public static void AddPageMessage(Skin skin, string heading, string message, string iconSrc)
+ [DnnDeprecated(10, 2, 0, "Please use IPageService.AddMessage")]
+ public static partial void AddPageMessage(Skin skin, string heading, string message, string iconSrc)
{
AddPageMessage(skin, heading, message, ModuleMessage.ModuleMessageType.GreenSuccess, iconSrc);
}
@@ -234,7 +236,8 @@ public static void AddPageMessage(Skin skin, string heading, string message, str
/// The Message Heading.
/// The Message Text.
/// The type of the message.
- public static void AddPageMessage(Skin skin, string heading, string message, ModuleMessage.ModuleMessageType moduleMessageType)
+ [DnnDeprecated(10, 2, 0, "Please use IPageService.AddMessage")]
+ public static partial void AddPageMessage(Skin skin, string heading, string message, ModuleMessage.ModuleMessageType moduleMessageType)
{
AddPageMessage(skin, heading, message, moduleMessageType, Null.NullString);
}
@@ -244,7 +247,8 @@ public static void AddPageMessage(Skin skin, string heading, string message, Mod
/// The Message Heading.
/// The Message Text.
/// The type of the message.
- public static void AddPageMessage(Page page, string heading, string message, ModuleMessage.ModuleMessageType moduleMessageType)
+ [DnnDeprecated(10, 2, 0, "Please use IPageService.AddMessage")]
+ public static partial void AddPageMessage(Page page, string heading, string message, ModuleMessage.ModuleMessageType moduleMessageType)
{
AddPageMessage(page, heading, message, moduleMessageType, Null.NullString);
}
@@ -474,6 +478,26 @@ public void RegisterModuleActionEvent(int moduleId, ActionEventHandler e)
this.ActionEventListeners.Add(new ModuleActionEventListener(moduleId, e));
}
+ /// AddPageMessage adds a Page Message control to the Skin.
+ /// The control.
+ /// The Message Heading.
+ /// The Message Text.
+ /// The type of the message.
+ /// The Icon to display.
+ internal static void AddPageMessage(Control control, string heading, string message, ModuleMessage.ModuleMessageType moduleMessageType, string iconSrc)
+ {
+ if (!string.IsNullOrEmpty(message))
+ {
+ Control contentPane = FindControlRecursive(control, Globals.glbDefaultPane);
+
+ if (contentPane != null)
+ {
+ ModuleMessage moduleMessage = GetModuleMessageControl(heading, message, moduleMessageType, iconSrc);
+ contentPane.Controls.AddAt(0, moduleMessage);
+ }
+ }
+ }
+
///
protected override void OnInit(EventArgs e)
{
@@ -491,7 +515,7 @@ protected override void OnInit(EventArgs e)
// Register any error messages on the Skin
if (this.Request.QueryString["error"] != null && this.hostSettings.ShowCriticalErrors)
{
- AddPageMessage(this, Localization.GetString("CriticalError.Error"), " ", ModuleMessage.ModuleMessageType.RedError);
+ AddPageMessage(this, Localization.GetString("CriticalError.Error"), " ", ModuleMessage.ModuleMessageType.RedError, string.Empty);
if (UserController.Instance.GetCurrentUserInfo().IsSuperUser)
{
@@ -507,7 +531,7 @@ protected override void OnInit(EventArgs e)
if (!TabPermissionController.CanAdminPage() && !success)
{
// only display the warning to non-administrators (administrators will see the errors)
- AddPageMessage(this, Localization.GetString("ModuleLoadWarning.Error"), string.Format(Localization.GetString("ModuleLoadWarning.Text"), this.PortalSettings.Email), ModuleMessage.ModuleMessageType.YellowWarning);
+ AddPageMessage(this, Localization.GetString("ModuleLoadWarning.Error"), string.Format(Localization.GetString("ModuleLoadWarning.Text"), this.PortalSettings.Email), ModuleMessage.ModuleMessageType.YellowWarning, string.Empty);
}
this.InvokeSkinEvents(SkinEventType.OnSkinInit);
@@ -520,7 +544,7 @@ protected override void OnInit(EventArgs e)
messageType = (ModuleMessage.ModuleMessageType)Enum.Parse(typeof(ModuleMessage.ModuleMessageType), HttpContextSource.Current.Items[OnInitMessageType].ToString(), true);
}
- AddPageMessage(this, string.Empty, HttpContextSource.Current.Items[OnInitMessage].ToString(), messageType);
+ AddPageMessage(this, string.Empty, HttpContextSource.Current.Items[OnInitMessage].ToString(), messageType, string.Empty);
this.javaScript.RequestRegistration(CommonJs.DnnPlugins);
ServicesFramework.Instance.RequestAjaxAntiForgerySupport();
@@ -604,20 +628,6 @@ private static void AddModuleMessage(Control control, string heading, string mes
}
}
- private static void AddPageMessage(Control control, string heading, string message, ModuleMessage.ModuleMessageType moduleMessageType, string iconSrc)
- {
- if (!string.IsNullOrEmpty(message))
- {
- Control contentPane = FindControlRecursive(control, Globals.glbDefaultPane);
-
- if (contentPane != null)
- {
- ModuleMessage moduleMessage = GetModuleMessageControl(heading, message, moduleMessageType, iconSrc);
- contentPane.Controls.AddAt(0, moduleMessage);
- }
- }
- }
-
private static Control FindControlRecursive(Control rootControl, string controlId)
{
if (rootControl.ID == controlId)
@@ -837,7 +847,7 @@ private void HandleAccessDenied(bool redirect = false)
}
else
{
- AddPageMessage(this, string.Empty, message, ModuleMessage.ModuleMessageType.YellowWarning);
+ AddPageMessage(this, string.Empty, message, ModuleMessage.ModuleMessageType.YellowWarning, string.Empty);
}
}
@@ -891,7 +901,8 @@ private bool ProcessMasterModules()
this,
string.Empty,
string.Format(Localization.GetString("ContractExpired.Error"), this.PortalSettings.PortalName, Globals.GetMediumDate(this.PortalSettings.ExpiryDate.ToString(CultureInfo.InvariantCulture)), this.PortalSettings.Email),
- ModuleMessage.ModuleMessageType.RedError);
+ ModuleMessage.ModuleMessageType.RedError,
+ string.Empty);
}
}
else
diff --git a/DNN Platform/Website/Default.aspx.cs b/DNN Platform/Website/Default.aspx.cs
index 4a6df5cda09..d769db2bdf1 100644
--- a/DNN Platform/Website/Default.aspx.cs
+++ b/DNN Platform/Website/Default.aspx.cs
@@ -17,6 +17,7 @@ namespace DotNetNuke.Framework
using DotNetNuke.Abstractions;
using DotNetNuke.Abstractions.Application;
using DotNetNuke.Abstractions.Logging;
+ using DotNetNuke.Abstractions.Pages;
using DotNetNuke.Abstractions.Portals;
using DotNetNuke.Common.Utilities;
using DotNetNuke.Entities.Portals;
@@ -29,6 +30,7 @@ namespace DotNetNuke.Framework
using DotNetNuke.Services.FileSystem;
using DotNetNuke.Services.Installer.Blocker;
using DotNetNuke.Services.Localization;
+ using DotNetNuke.Services.Pages;
using DotNetNuke.Services.Personalization;
using DotNetNuke.UI;
using DotNetNuke.UI.Internals;
@@ -59,11 +61,12 @@ public partial class DefaultPage : CDefault, IClientAPICallbackEventHandler
private readonly IHostSettingsService hostSettingsService;
private readonly IEventLogger eventLogger;
private readonly IPortalSettingsController portalSettingsController;
+ private readonly IPageService pageService;
/// Initializes a new instance of the class.
[Obsolete("Deprecated in DotNetNuke 10.0.2. Please use overload with INavigationManager. Scheduled removal in v12.0.0.")]
public DefaultPage()
- : this(null, null, null, null, null, null, null, null, null)
+ : this(null, null, null, null, null, null, null, null, null, null)
{
}
@@ -77,7 +80,8 @@ public DefaultPage()
/// The event logger.
/// The portal controller.
/// The portal settings controller.
- public DefaultPage(INavigationManager navigationManager, IApplicationInfo appInfo, IApplicationStatusInfo appStatus, IModuleControlPipeline moduleControlPipeline, IHostSettings hostSettings, IHostSettingsService hostSettingsService, IEventLogger eventLogger, IPortalController portalController, IPortalSettingsController portalSettingsController)
+ /// The page service.
+ public DefaultPage(INavigationManager navigationManager, IApplicationInfo appInfo, IApplicationStatusInfo appStatus, IModuleControlPipeline moduleControlPipeline, IHostSettings hostSettings, IHostSettingsService hostSettingsService, IEventLogger eventLogger, IPortalController portalController, IPortalSettingsController portalSettingsController, IPageService pageService)
: base(portalController, appStatus, hostSettings)
{
this.NavigationManager = navigationManager ?? Globals.GetCurrentServiceProvider().GetRequiredService();
@@ -88,6 +92,7 @@ public DefaultPage(INavigationManager navigationManager, IApplicationInfo appInf
this.hostSettingsService = hostSettingsService ?? Globals.GetCurrentServiceProvider().GetRequiredService();
this.eventLogger = eventLogger ?? Globals.GetCurrentServiceProvider().GetRequiredService();
this.portalSettingsController = portalSettingsController ?? Globals.GetCurrentServiceProvider().GetRequiredService();
+ this.pageService = pageService ?? Globals.GetCurrentServiceProvider().GetRequiredService();
}
public string CurrentSkinPath => ((PortalSettings)HttpContext.Current.Items["PortalSettings"]).ActiveTab.SkinPath;
@@ -228,11 +233,12 @@ protected override void OnInit(EventArgs e)
{
var heading = Localization.GetString("PageDisabled.Header");
var message = Localization.GetString("PageDisabled.Text");
- UI.Skins.Skin.AddPageMessage(
- ctlSkin,
+ this.pageService.AddMessage(new PageMessage(
heading,
message,
- ModuleMessage.ModuleMessageType.YellowWarning);
+ PageMessageType.Warning,
+ string.Empty,
+ PagePriority.Page));
}
else
{
@@ -337,6 +343,13 @@ protected override void OnPreRender(EventArgs evt)
this.metaPanel.Visible = !UrlUtils.InPopUp();
if (!UrlUtils.InPopUp())
{
+ this.pageService.SetTitle(this.Title, PagePriority.Page);
+ this.Title = this.pageService.GetTitle();
+ this.pageService.SetDescription(this.Description, PagePriority.Page);
+ this.Description = this.pageService.GetDescription();
+ this.pageService.SetKeyWords(this.KeyWords, PagePriority.Page);
+ this.KeyWords = this.pageService.GetKeyWords();
+
this.MetaGenerator.Content = this.Generator;
this.MetaGenerator.Visible = !string.IsNullOrEmpty(this.Generator);
this.MetaAuthor.Content = this.PortalSettings.PortalName;
@@ -352,6 +365,8 @@ protected override void OnPreRender(EventArgs evt)
this.Page.Response.AddHeader("X-UA-Compatible", this.PortalSettings.AddCompatibleHttpHeader);
}
+ this.pageService.SetCanonicalLinkUrl(this.CanonicalLinkUrl, PagePriority.Page);
+ this.CanonicalLinkUrl = this.pageService.GetCanonicalLinkUrl();
if (!string.IsNullOrEmpty(this.CanonicalLinkUrl))
{
// Add Canonical using the primary alias
@@ -362,6 +377,21 @@ protected override void OnPreRender(EventArgs evt)
// Add the HtmlLink to the Head section of the page.
this.Page.Header.Controls.Add(canonicalLink);
}
+
+ foreach (var item in this.pageService.GetHeadTags())
+ {
+ this.Page.Header.Controls.Add(new LiteralControl(item.Value));
+ }
+
+ foreach (var item in this.pageService.GetMetaTags())
+ {
+ this.Page.Header.Controls.Add(new Meta() { Name = item.Name, Content = item.Content });
+ }
+
+ foreach (var item in this.pageService.GetMessages())
+ {
+ Skin.AddPageMessage(this, item.Heading, item.Message, item.MessageType.ToModuleMessageType(), item.IconSrc);
+ }
}
///