diff --git a/src/Simplify.Web/PageComposition/Stages/ContextVariablesSetter.cs b/src/Simplify.Web/PageComposition/Stages/ContextVariablesSetter.cs
new file mode 100644
index 00000000..08838850
--- /dev/null
+++ b/src/Simplify.Web/PageComposition/Stages/ContextVariablesSetter.cs
@@ -0,0 +1,24 @@
+using Simplify.Web.Modules.Context;
+
+namespace Simplify.Web.PageComposition.Stages;
+
+public class ContextVariablesSetter(IWebContextProvider webContextProvider) : IPageCompositionStage
+{
+ ///
+ /// The site variable name site URL.
+ ///
+ public const string VariableNameSiteUrl = "SV:SiteUrl";
+
+ ///
+ /// The site variable name site virtual path (returns '/yoursite' if your site is placed in virtual directory like http://yourdomain.com/yoursite/).
+ ///
+ public const string VariableNameSiteVirtualPath = "~";
+
+ public void Execute(Modules.Data.IDataCollector dataCollector)
+ {
+ var context = webContextProvider.Get();
+
+ dataCollector.Add(VariableNameSiteUrl, context.SiteUrl);
+ dataCollector.Add(VariableNameSiteVirtualPath, context.VirtualPath);
+ }
+}
\ No newline at end of file
diff --git a/src/Simplify.Web/PageComposition/Stages/EnvironmentVariablesInjectionStage.cs b/src/Simplify.Web/PageComposition/Stages/EnvironmentVariablesInjectionStage.cs
new file mode 100644
index 00000000..065e7293
--- /dev/null
+++ b/src/Simplify.Web/PageComposition/Stages/EnvironmentVariablesInjectionStage.cs
@@ -0,0 +1,23 @@
+using Simplify.Web.Modules.ApplicationEnvironment;
+using Simplify.Web.Modules.Data;
+
+namespace Simplify.Web.PageComposition.Stages;
+
+public class EnvironmentVariablesInjectionStage(IDynamicEnvironment dynamicEnvironment) : IPageCompositionStage
+{
+ ///
+ /// The site variable name templates directory.
+ ///
+ public const string VariableNameTemplatesPath = "SV:TemplatesDir";
+
+ ///
+ /// The site variable name current style.
+ ///
+ public const string VariableNameSiteStyle = "SV:Style";
+
+ public void Execute(IDataCollector dataCollector)
+ {
+ dataCollector.Add(VariableNameTemplatesPath, dynamicEnvironment.TemplatesPath);
+ dataCollector.Add(VariableNameSiteStyle, dynamicEnvironment.SiteStyle);
+ }
+}
\ No newline at end of file
diff --git a/src/Simplify.Web/PageComposition/Stages/LanguageInjectionStage.cs b/src/Simplify.Web/PageComposition/Stages/LanguageInjectionStage.cs
new file mode 100644
index 00000000..a30f70b1
--- /dev/null
+++ b/src/Simplify.Web/PageComposition/Stages/LanguageInjectionStage.cs
@@ -0,0 +1,48 @@
+using System.Threading;
+using Simplify.Web.Modules.Data;
+using Simplify.Web.Modules.Localization;
+
+namespace Simplify.Web.PageComposition.Stages;
+
+public class LanguageInjectionStage(ILanguageManagerProvider languageManagerProvider) : IPageCompositionStage
+{
+ ///
+ /// The site variable name current language.
+ ///
+ public const string VariableNameCurrentLanguage = "SV:Language";
+
+ ///
+ /// The site variable name current language extension.
+ ///
+ public const string VariableNameCurrentLanguageExtension = "SV:LanguageExt";
+
+ ///
+ /// The site variable name current language culture name.
+ ///
+ public const string VariableNameCurrentLanguageCultureName = "SV:LanguageCultureName";
+
+ ///
+ /// The site variable name current language culture name extension.
+ ///
+ public const string VariableNameCurrentLanguageCultureNameExtension = "SV:LanguageCultureNameExt";
+
+ public void Execute(IDataCollector dataCollector)
+ {
+ var languageManager = languageManagerProvider.Get();
+
+ if (!string.IsNullOrEmpty(languageManager.Language))
+ {
+ dataCollector.Add(VariableNameCurrentLanguage, languageManager.Language);
+ dataCollector.Add(VariableNameCurrentLanguageExtension, "." + languageManager.Language);
+ dataCollector.Add(VariableNameCurrentLanguageCultureName, Thread.CurrentThread.CurrentCulture.TextInfo.CultureName);
+ dataCollector.Add(VariableNameCurrentLanguageCultureNameExtension, "." + Thread.CurrentThread.CurrentCulture.TextInfo.CultureName);
+ }
+ else
+ {
+ dataCollector.Add(VariableNameCurrentLanguage, (string?)null);
+ dataCollector.Add(VariableNameCurrentLanguageExtension, (string?)null);
+ dataCollector.Add(VariableNameCurrentLanguageCultureName, (string?)null);
+ dataCollector.Add(VariableNameCurrentLanguageCultureNameExtension, (string?)null);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Simplify.Web/PageComposition/Stages/SiteTitleInjectionStage.cs b/src/Simplify.Web/PageComposition/Stages/SiteTitleInjectionStage.cs
new file mode 100644
index 00000000..9c460dcc
--- /dev/null
+++ b/src/Simplify.Web/PageComposition/Stages/SiteTitleInjectionStage.cs
@@ -0,0 +1,30 @@
+using Simplify.Web.Modules.Context;
+using Simplify.Web.Modules.Data;
+
+namespace Simplify.Web.PageComposition.Stages;
+
+public class SiteTitleInjectionStage(IWebContextProvider webContextProvider, IStringTable stringTable) : IPageCompositionStage
+{
+ ///
+ /// The site title string table variable name.
+ ///
+ public const string SiteTitleStringTableVariableName = "SiteTitle";
+
+ public void Execute(IDataCollector dataCollector)
+ {
+ var context = webContextProvider.Get();
+ var currentPath = context.Request.Path.Value;
+ var siteTitle = stringTable.GetItem(SiteTitleStringTableVariableName);
+
+ if (string.IsNullOrEmpty(siteTitle))
+ return;
+
+ if (string.IsNullOrEmpty(currentPath) ||
+ currentPath == "/" ||
+ currentPath!.StartsWith("/?") ||
+ !dataCollector.IsDataExist(dataCollector.TitleVariableName))
+ dataCollector.Add(dataCollector.TitleVariableName, siteTitle);
+ else
+ dataCollector.Add(dataCollector.TitleVariableName, " - " + siteTitle);
+ }
+}
\ No newline at end of file
diff --git a/src/Simplify.Web/PageComposition/Stages/StopwatchDataInjectionStage.cs b/src/Simplify.Web/PageComposition/Stages/StopwatchDataInjectionStage.cs
new file mode 100644
index 00000000..37beeff1
--- /dev/null
+++ b/src/Simplify.Web/PageComposition/Stages/StopwatchDataInjectionStage.cs
@@ -0,0 +1,11 @@
+using Simplify.Web.Diagnostics.Measurement;
+
+namespace Simplify.Web.PageComposition.Stages;
+
+public class StopwatchDataInjectionStage(IStopwatchProvider stopwatchProvider) : IPageCompositionStage
+{
+ public const string VariableNameExecutionTime = "SV:SiteExecutionTime";
+
+ public void Execute(Modules.Data.IDataCollector dataCollector) =>
+ dataCollector.Add(VariableNameExecutionTime, stopwatchProvider.StopAndGetMeasurement().ToString("mm\\:ss\\:fff"));
+}
\ No newline at end of file
diff --git a/src/Simplify.Web/PageComposition/Stages/StringTableItemsInjectionStage.cs b/src/Simplify.Web/PageComposition/Stages/StringTableItemsInjectionStage.cs
new file mode 100644
index 00000000..30edd5b8
--- /dev/null
+++ b/src/Simplify.Web/PageComposition/Stages/StringTableItemsInjectionStage.cs
@@ -0,0 +1,17 @@
+using System.Collections.Generic;
+using Simplify.Web.Modules.Data;
+
+namespace Simplify.Web.PageComposition.Stages;
+
+public class StringTableItemsInjectionStage(IStringTable stringTable) : IPageCompositionStage
+{
+ private const string StringTablePrefix = "StringTable.";
+
+ private readonly IStringTable _stringTable = stringTable;
+
+ public void Execute(IDataCollector dataCollector)
+ {
+ foreach (var item in (IDictionary)_stringTable.Items)
+ dataCollector.Add(StringTablePrefix + item.Key, item.Value.ToString());
+ }
+}
\ No newline at end of file