using System.Globalization;
using Microsoft.Extensions.Options;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.PublishedContent;
using Umbraco.Cms.Core.Web;
namespace Umbraco.Cms.Core.Routing;
// TODO: Kill this, but we need to port all of it's functionality
public class PublishedRequestOld // : IPublishedRequest
{
private readonly IPublishedRouter _publishedRouter;
private readonly WebRoutingSettings _webRoutingSettings;
private CultureInfo? _culture;
private DomainAndUri? _domain;
private bool _is404;
private IPublishedContent? _publishedContent;
private bool _readonly; // after prepared
///
/// Initializes a new instance of the class.
///
public PublishedRequestOld(IPublishedRouter publishedRouter, IUmbracoContext umbracoContext, IOptions webRoutingSettings, Uri? uri = null)
{
UmbracoContext = umbracoContext ?? throw new ArgumentNullException(nameof(umbracoContext));
_publishedRouter = publishedRouter ?? throw new ArgumentNullException(nameof(publishedRouter));
_webRoutingSettings = webRoutingSettings.Value;
Uri = uri ?? umbracoContext.CleanedUmbracoUrl;
}
///
/// Gets the UmbracoContext.
///
public IUmbracoContext UmbracoContext { get; }
///
/// Gets or sets the cleaned up Uri used for routing.
///
/// The cleaned up Uri has no virtual directory, no trailing slash, no .aspx extension, etc.
public Uri Uri { get; }
public bool CacheabilityNoCache { get; set; }
/////
///// Prepares the request.
/////
// public void Prepare()
// {
// _publishedRouter.PrepareRequest(this);
// }
///
/// Gets or sets a value indicating whether the Umbraco Backoffice should ignore a collision for this request.
///
public bool IgnorePublishedContentCollisions { get; set; }
///
/// Gets or sets the template model to use to display the requested content.
///
public ITemplate? Template { get; }
///
/// Gets the alias of the template to use to display the requested content.
///
public string? TemplateAlias => Template?.Alias;
///
/// Gets or sets the content request's domain.
///
///
/// Is a DomainAndUri object ie a standard Domain plus the fully qualified uri. For example,
/// the Domain may contain "example.com" whereas the Uri will be fully qualified eg
/// "http://example.com/".
///
public DomainAndUri? Domain
{
get => _domain;
set
{
EnsureWriteable();
_domain = value;
}
}
///
/// Gets a value indicating whether the content request has a domain.
///
public bool HasDomain => Domain != null;
///
/// Gets or sets the content request's culture.
///
public CultureInfo Culture
{
get => _culture ?? Thread.CurrentThread.CurrentCulture;
set
{
EnsureWriteable();
_culture = value;
}
}
// utility for ensuring it is ok to set some properties
public void EnsureWriteable()
{
if (_readonly)
{
throw new InvalidOperationException("Cannot modify a PublishedRequest once it is read-only.");
}
}
// #region Events
/////
///// Triggers before the published content request is prepared.
/////
///// When the event triggers, no preparation has been done. It is still possible to
///// modify the request's Uri property, for example to restore its original, public-facing value
///// that might have been modified by an in-between equipment such as a load-balancer.
// public static event EventHandler Preparing;
/////
///// Triggers once the published content request has been prepared, but before it is processed.
/////
///// When the event triggers, preparation is done ie domain, culture, document, template,
///// rendering engine, etc. have been setup. It is then possible to change anything, before
///// the request is actually processed and rendered by Umbraco.
// public static event EventHandler Prepared;
/////
///// Triggers the Preparing event.
/////
// public void OnPreparing()
// {
// Preparing?.Invoke(this, EventArgs.Empty);
// }
/////
///// Triggers the Prepared event.
/////
// public void OnPrepared()
// {
// Prepared?.Invoke(this, EventArgs.Empty);
// if (HasPublishedContent == false)
// Is404 = true; // safety
// _readonly = true;
// }
// #endregion
#region PublishedContent
/////
///// Gets or sets the requested content.
/////
///// Setting the requested content clears Template.
// public IPublishedContent PublishedContent
// {
// get { return _publishedContent; }
// set
// {
// EnsureWriteable();
// _publishedContent = value;
// IsInternalRedirectPublishedContent = false;
// TemplateModel = null;
// }
// }
///
/// Sets the requested content, following an internal redirect.
///
/// The requested content.
///
/// Depending on UmbracoSettings.InternalRedirectPreservesTemplate, will
/// preserve or reset the template, if any.
///
public void SetInternalRedirectPublishedContent(IPublishedContent content)
{
// if (content == null)
// throw new ArgumentNullException(nameof(content));
// EnsureWriteable();
//// unless a template has been set already by the finder,
//// template should be null at that point.
//// IsInternalRedirect if IsInitial, or already IsInternalRedirect
// var isInternalRedirect = IsInitialPublishedContent || IsInternalRedirectPublishedContent;
//// redirecting to self
// if (content.Id == PublishedContent.Id) // neither can be null
// {
// // no need to set PublishedContent, we're done
// IsInternalRedirectPublishedContent = isInternalRedirect;
// return;
// }
//// else
//// save
// var template = Template;
//// set published content - this resets the template, and sets IsInternalRedirect to false
// PublishedContent = content;
// IsInternalRedirectPublishedContent = isInternalRedirect;
//// must restore the template if it's an internal redirect & the config option is set
// if (isInternalRedirect && _webRoutingSettings.InternalRedirectPreservesTemplate)
// {
// // restore
// TemplateModel = template;
// }
}
///
/// Gets the initial requested content.
///
///
/// The initial requested content is the content that was found by the finders,
/// before anything such as 404, redirect... took place.
///
public IPublishedContent? InitialPublishedContent { get; private set; }
///
/// Gets value indicating whether the current published content is the initial one.
///
public bool IsInitialPublishedContent =>
InitialPublishedContent != null && InitialPublishedContent == _publishedContent;
///
/// Indicates that the current PublishedContent is the initial one.
///
public void SetIsInitialPublishedContent()
{
EnsureWriteable();
// note: it can very well be null if the initial content was not found
InitialPublishedContent = _publishedContent;
IsInternalRedirectPublishedContent = false;
}
///
/// Gets or sets a value indicating whether the current published content has been obtained
/// from the initial published content following internal redirections exclusively.
///
///
/// Used by PublishedContentRequestEngine.FindTemplate() to figure out whether to
/// apply the internal redirect or not, when content is not the initial content.
///
public bool IsInternalRedirectPublishedContent { get; private set; }
#endregion
// note: do we want to have an ordered list of alternate cultures,
// to allow for fallbacks when doing dictionary lookup and such?
#region Status
///
/// Gets or sets a value indicating whether the requested content could not be found.
///
///
/// This is set in the PublishedContentRequestBuilder and can also be used in
/// custom content finders or Prepared event handlers, where we want to allow developers
/// to indicate a request is 404 but not to cancel it.
///
public bool Is404
{
get => _is404;
set
{
EnsureWriteable();
_is404 = value;
}
}
///
/// Gets a value indicating whether the content request triggers a redirect (permanent or not).
///
public bool IsRedirect => string.IsNullOrWhiteSpace(RedirectUrl) == false;
///
/// Gets or sets a value indicating whether the redirect is permanent.
///
public bool IsRedirectPermanent { get; private set; }
///
/// Gets or sets the URL to redirect to, when the content request triggers a redirect.
///
public string? RedirectUrl { get; private set; }
///
/// Indicates that the content request should trigger a redirect (302).
///
/// The URL to redirect to.
///
/// Does not actually perform a redirect, only registers that the response should
/// redirect. Redirect will or will not take place in due time.
///
public void SetRedirect(string url)
{
EnsureWriteable();
RedirectUrl = url;
IsRedirectPermanent = false;
}
///
/// Indicates that the content request should trigger a permanent redirect (301).
///
/// The URL to redirect to.
///
/// Does not actually perform a redirect, only registers that the response should
/// redirect. Redirect will or will not take place in due time.
///
public void SetRedirectPermanent(string url)
{
EnsureWriteable();
RedirectUrl = url;
IsRedirectPermanent = true;
}
///
/// Indicates that the content request should trigger a redirect, with a specified status code.
///
/// The URL to redirect to.
/// The status code (300-308).
///
/// Does not actually perform a redirect, only registers that the response should
/// redirect. Redirect will or will not take place in due time.
///
public void SetRedirect(string url, int status)
{
EnsureWriteable();
if (status < 300 || status > 308)
{
throw new ArgumentOutOfRangeException(nameof(status), "Valid redirection status codes 300-308.");
}
RedirectUrl = url;
IsRedirectPermanent = status == 301 || status == 308;
// default redirect statuses
if (status != 301 && status != 302)
{
ResponseStatusCode = status;
}
}
///
/// Gets or sets the content request http response status code.
///
///
/// Does not actually set the http response status code, only registers that the response
/// should use the specified code. The code will or will not be used, in due time.
///
public int ResponseStatusCode { get; private set; }
///
/// Gets or sets the content request http response status description.
///
///
/// Does not actually set the http response status description, only registers that the response
/// should use the specified description. The description will or will not be used, in due time.
///
public string? ResponseStatusDescription { get; private set; }
///
/// Sets the http response status code, along with an optional associated description.
///
/// The http status code.
/// The description.
///
/// Does not actually set the http response status code and description, only registers that
/// the response should use the specified code and description. The code and description will or will
/// not be used, in due time.
///
public void SetResponseStatus(int code, string? description = null)
{
EnsureWriteable();
// .Status is deprecated
// .SubStatusCode is IIS 7+ internal, ignore
ResponseStatusCode = code;
ResponseStatusDescription = description;
}
#endregion
#region Response Cache
// ///
// /// Gets or sets the System.Web.HttpCacheability
// ///
// Note: we used to set a default value here but that would then be the default
// for ALL requests, we shouldn't overwrite it though if people are using [OutputCache] for example
// see: https://our.umbraco.com/forum/using-umbraco-and-getting-started/79715-output-cache-in-umbraco-752
// public HttpCacheability Cacheability { get; set; }
///
/// Gets or sets a list of Extensions to append to the Response.Cache object.
///
public List CacheExtensions { get; set; } = new();
///
/// Gets or sets a dictionary of Headers to append to the Response object.
///
public Dictionary Headers { get; set; } = new();
#endregion
}