@namespace Oqtane.Components
@using System.Net
@using System.Security.Claims
@using Microsoft.AspNetCore.Http
@using Microsoft.AspNetCore.Http.Extensions
@using Microsoft.AspNetCore.Antiforgery
@using Microsoft.AspNetCore.Localization
@using Microsoft.Net.Http.Headers
@using Microsoft.Extensions.Primitives
@using Oqtane.Client
@using Oqtane.UI
@using Oqtane.Repository
@using Oqtane.Infrastructure
@using Oqtane.Security
@using Oqtane.Models
@using Oqtane.Shared
@using Oqtane.Themes
@using Oqtane.Extensions
@using System.Globalization
@inject NavigationManager NavigationManager
@inject IAntiforgery Antiforgery
@inject IConfigManager ConfigManager
@inject ITenantManager TenantManager
@inject ISiteService SiteService
@inject IPageRepository PageRepository
@inject IThemeRepository ThemeRepository
@inject ILanguageRepository LanguageRepository
@inject ILocalizationManager LocalizationManager
@inject IAliasRepository AliasRepository
@inject IUrlMappingRepository UrlMappingRepository
@inject IVisitorRepository VisitorRepository
@inject IJwtManager JwtManager
@if (_initialized)
{
@if (_scripts.Contains("PWA Manifest"))
{
}
@((MarkupString)_styleSheets)
@if (_renderMode == RenderModes.Static)
{
@if (string.IsNullOrEmpty(_message))
{
@if (_renderMode == RenderModes.Static)
{
}
else
{
}
@((MarkupString)_scripts)
@((MarkupString)_bodyResources)
}
else
{
@_message
}
}
@code {
private bool _initialized = false;
private string _renderMode = RenderModes.Interactive;
private string _runtime = Runtimes.Server;
private bool _prerender = true;
private int _visitorId = -1;
private string _antiForgeryToken = "";
private string _remoteIPAddress = "";
private string _platform = "";
private string _authorizationToken = "";
private string _language = "en";
private string _headResources = "";
private string _bodyResources = "";
private string _styleSheets = "";
private string _scripts = "";
private string _message = "";
private PageState _pageState;
// CascadingParameter is required to access HttpContext
[CascadingParameter]
HttpContext Context { get; set; }
protected override async Task OnInitializedAsync()
{
_antiForgeryToken = Antiforgery.GetAndStoreTokens(Context).RequestToken;
_remoteIPAddress = Context.Connection.RemoteIpAddress?.ToString() ?? "";
_platform = System.Runtime.InteropServices.RuntimeInformation.OSDescription;
// if framework is installed
if (ConfigManager.IsInstalled())
{
var alias = TenantManager.GetAlias();
if (alias != null)
{
var url = WebUtility.UrlDecode(Context.Request.GetEncodedUrl());
if (!alias.IsDefault)
{
HandleDefaultAliasRedirect(alias, url);
}
var site = await SiteService.GetSiteAsync(alias.SiteId);
if (site != null && (!site.IsDeleted || url.Contains("admin/site")) && site.RenderMode != RenderModes.Headless)
{
_renderMode = site.RenderMode;
_runtime = site.Runtime;
_prerender = site.Prerender;
var modules = new List();
Route route = new Route(url, alias.Path);
var page = site.Pages.FirstOrDefault(item => item.Path.Equals(route.PagePath, StringComparison.OrdinalIgnoreCase));
if (page == null && route.PagePath == "") // naked path refers to site home page
{
if (site.HomePageId != null)
{
page = site.Pages.FirstOrDefault(item => item.PageId == site.HomePageId);
}
if (page == null)
{
// fallback to use the first page in the collection
page = site.Pages.FirstOrDefault();
}
}
if (page == null)
{
// personalized pages need to be retrieved using path
page = PageRepository.GetPage(route.PagePath, site.SiteId);
}
if (page == null || page.IsDeleted)
{
HandlePageNotFound(site, page, route);
}
else
{
modules = await SiteService.GetModulesAsync(site.SiteId, page.PageId);
}
if (site.VisitorTracking)
{
TrackVisitor(site.SiteId);
}
// get jwt token for downstream APIs
if (Context.User.Identity.IsAuthenticated)
{
CreateJwtToken(alias);
}
// includes resources
var resources = await GetPageResources(alias, site, page, modules, int.Parse(route.ModuleId, CultureInfo.InvariantCulture), route.Action);
ManageStyleSheets(resources);
ManageScripts(resources, alias);
// generate scripts
if (site.PwaIsEnabled && site.PwaAppIconFileId != null && site.PwaSplashIconFileId != null)
{
_scripts += CreatePWAScript(alias, site, route);
}
@if (_renderMode == RenderModes.Static)
{
_scripts += CreateScrollPositionScript();
}
_headResources += ParseScripts(site.HeadContent);
_bodyResources += ParseScripts(site.BodyContent);
// set culture if not specified
string cultureCookie = Context.Request.Cookies[Shared.CookieRequestCultureProvider.DefaultCookieName];
if (cultureCookie == null)
{
// get default language for site
if (site.Languages.Any())
{
// use default language if specified otherwise use first language in collection
cultureCookie = (site.Languages.Where(l => l.IsDefault).SingleOrDefault() ?? site.Languages.First()).Code;
}
else
{
// fallback language
cultureCookie = LocalizationManager.GetDefaultCulture();
}
// convert language code to culture cookie format (ie. "c=en|uic=en")
cultureCookie = Shared.CookieRequestCultureProvider.MakeCookieValue(new Models.RequestCulture(cultureCookie));
SetLocalizationCookie(cultureCookie);
}
// set language for page
if (!string.IsNullOrEmpty(cultureCookie))
{
_language = Shared.CookieRequestCultureProvider.ParseCookieValue(cultureCookie).Culture.Name;
}
// create initial PageState
_pageState = new PageState
{
Alias = alias,
Site = site,
Page = page,
Modules = modules,
User = null,
Uri = new Uri(url, UriKind.Absolute),
Route = route,
QueryString = Utilities.ParseQueryString(route.Query),
UrlParameters = route.UrlParameters,
ModuleId = -1,
Action = "",
EditMode = false,
LastSyncDate = DateTime.MinValue,
RenderMode = _renderMode,
Runtime = (Shared.Runtime)Enum.Parse(typeof(Shared.Runtime), _runtime),
VisitorId = _visitorId,
RemoteIPAddress = _remoteIPAddress,
ReturnUrl = "",
IsInternalNavigation = false,
RenderId = Guid.NewGuid(),
Refresh = true
};
}
else
{
_message = "Site Is Disabled";
}
}
else
{
_message = "Site Not Configured Correctly - No Matching Alias Exists For Host Name";
}
}
_initialized = true;
}
private void HandleDefaultAliasRedirect(Alias alias, string url)
{
// get aliases for site and tenant
var aliases = AliasRepository.GetAliases().Where(item => item.TenantId == alias.TenantId && item.SiteId == alias.SiteId);
// get first default alias
var defaultAlias = aliases.Where(item => item.IsDefault).FirstOrDefault();
if (defaultAlias != null)
{
// redirect to default alias
NavigationManager.NavigateTo(url.Replace(alias.Name, defaultAlias.Name), true);
}
else // no default alias specified - use first alias
{
defaultAlias = aliases.FirstOrDefault();
if (defaultAlias != null && alias.Name.Trim() != defaultAlias.Name.Trim())
{
// redirect to first alias
NavigationManager.NavigateTo(url.Replace(alias.Name, defaultAlias.Name), true);
}
}
}
private void HandlePageNotFound(Site site, Page page, Route route)
{
// page not found - look for url mapping
var urlMapping = UrlMappingRepository.GetUrlMapping(site.SiteId, route.PagePath);
if (urlMapping != null && !string.IsNullOrEmpty(urlMapping.MappedUrl))
{
// redirect to mapped url
var url = (urlMapping.MappedUrl.StartsWith("http")) ? urlMapping.MappedUrl : route.SiteUrl + "/" + urlMapping.MappedUrl;
NavigationManager.NavigateTo(url, true);
}
else // no url mapping exists
{
if (route.PagePath != "404")
{
// redirect to 404 page
NavigationManager.NavigateTo(route.SiteUrl + "/404", true);
}
}
}
private void TrackVisitor(int SiteId)
{
try
{
// get request attributes
string useragent = (Context.Request.Headers[HeaderNames.UserAgent] != StringValues.Empty) ? Context.Request.Headers[HeaderNames.UserAgent] : "(none)";
useragent = (useragent.Length > 256) ? useragent.Substring(0, 256) : useragent;
string language = (Context.Request.Headers[HeaderNames.AcceptLanguage] != StringValues.Empty) ? Context.Request.Headers[HeaderNames.AcceptLanguage] : "";
language = (language.Contains(",")) ? language.Substring(0, language.IndexOf(",")) : language;
language = (language.Contains(";")) ? language.Substring(0, language.IndexOf(";")) : language;
language = (language.Trim().Length == 0) ? "??" : language;
// filter
var settings = Context.GetSiteSettings();
var filter = settings.GetValue("VisitorFilter", Constants.DefaultVisitorFilter);
foreach (string term in filter.ToLower().Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(sValue => sValue.Trim()).ToArray())
{
if (_remoteIPAddress.ToLower().Contains(term) || useragent.ToLower().Contains(term) || language.ToLower().Contains(term))
{
return;
}
}
// get other request attributes
string url = Context.Request.GetEncodedUrl();
string referrer = (Context.Request.Headers[HeaderNames.Referer] != StringValues.Empty) ? Context.Request.Headers[HeaderNames.Referer] : "";
int? userid = Context.User.UserId();
userid = (userid == -1) ? null : userid;
// get cookie value
var visitorCookieName = Constants.VisitorCookiePrefix + SiteId.ToString();
var visitorCookieValue = Context.Request.Cookies[visitorCookieName];
DateTime expiry = DateTime.MinValue;
if (visitorCookieValue != null && visitorCookieValue.Contains("|"))
{
var values = visitorCookieValue.Split('|');
int.TryParse(values[0], out _visitorId);
DateTime.TryParseExact(values[1], "M/d/yyyy hh:mm:ss tt", CultureInfo.InvariantCulture, DateTimeStyles.None, out expiry);
}
else // legacy cookie format
{
int.TryParse(visitorCookieValue, out _visitorId);
}
bool setcookie = false;
Visitor visitor = null;
if (_visitorId <= 0)
{
// if enabled use IP Address correlation
var correlate = bool.Parse(settings.GetValue("VisitorCorrelation", "true"));
if (correlate)
{
visitor = VisitorRepository.GetVisitor(SiteId, _remoteIPAddress);
if (visitor != null)
{
_visitorId = visitor.VisitorId;
setcookie = true;
}
}
}
if (_visitorId <= 0)
{
// create new visitor
visitor = new Visitor();
visitor.SiteId = SiteId;
visitor.IPAddress = _remoteIPAddress;
visitor.UserAgent = useragent;
visitor.Language = language;
visitor.Url = url;
visitor.Referrer = referrer;
visitor.UserId = userid;
visitor.Visits = 1;
visitor.CreatedOn = DateTime.UtcNow;
visitor.VisitedOn = DateTime.UtcNow;
visitor = VisitorRepository.AddVisitor(visitor);
_visitorId = visitor.VisitorId;
setcookie = true;
}
else
{
// check expiry
if (DateTime.UtcNow > expiry)
{
if (visitor == null)
{
// get visitor if not previously loaded
visitor = VisitorRepository.GetVisitor(_visitorId);
}
if (visitor != null)
{
// update visitor
visitor.IPAddress = _remoteIPAddress;
visitor.UserAgent = useragent;
visitor.Language = language;
visitor.Url = url;
if (!string.IsNullOrEmpty(referrer))
{
visitor.Referrer = referrer;
}
if (userid != null)
{
visitor.UserId = userid;
}
visitor.Visits += 1;
visitor.VisitedOn = DateTime.UtcNow;
VisitorRepository.UpdateVisitor(visitor);
setcookie = true;
}
else
{
// remove cookie if visitor does not exist
Context.Response.Cookies.Delete(visitorCookieName);
}
}
}
// set cookie
if (setcookie)
{
expiry = DateTime.UtcNow.AddMinutes(int.Parse(settings.GetValue("VisitorDuration", "5")));
Context.Response.Cookies.Append(
visitorCookieName,
$"{_visitorId}|{expiry.ToString("M/d/yyyy hh:mm:ss tt", CultureInfo.InvariantCulture)}",
new CookieOptions()
{
Expires = DateTimeOffset.UtcNow.AddYears(10),
IsEssential = true,
SameSite = Microsoft.AspNetCore.Http.SameSiteMode.Lax, // Set SameSite attribute
Secure = true, // Ensure the cookie is only sent over HTTPS
HttpOnly = true // Optional: Helps mitigate XSS attacks
}
);
}
}
catch
{
// error tracking visitor
}
}
private void CreateJwtToken(Alias alias)
{
var sitesettings = Context.GetSiteSettings();
var secret = sitesettings.GetValue("JwtOptions:Secret", "");
if (!string.IsNullOrEmpty(secret))
{
_authorizationToken = JwtManager.GenerateToken(alias, (ClaimsIdentity)Context.User.Identity, secret, sitesettings.GetValue("JwtOptions:Issuer", ""), sitesettings.GetValue("JwtOptions:Audience", ""), int.Parse(sitesettings.GetValue("JwtOptions:Lifetime", "20")));
}
}
private string CreatePWAScript(Alias alias, Site site, Route route)
{
return Environment.NewLine +
"" + Environment.NewLine +
"" + Environment.NewLine;
}
private string CreateScrollPositionScript()
{
return Environment.NewLine +
"" + Environment.NewLine;
}
private string ParseScripts(string content)
{
var scripts = "";
// in interactive render mode, parse scripts from content and inject into page
if (_renderMode == RenderModes.Interactive && !string.IsNullOrEmpty(content))
{
var index = content.IndexOf("", index) + 9 - index);
index = content.IndexOf("