using System; using System.Collections.Generic; using System.Globalization; using System.Threading; 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 bool _readonly; // after prepared private bool _is404; private DomainAndUri _domain; private CultureInfo _culture; private IPublishedContent _publishedContent; private IPublishedContent _initialPublishedContent; // found by finders before 404, redirects, etc /// /// 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; } // 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."); } } 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; } //#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 => _initialPublishedContent; /// /// 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 /// /// 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 { return _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 { return _culture ?? Thread.CurrentThread.CurrentCulture; } set { EnsureWriteable(); _culture = value; } } // 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 { return _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); if (status != 301 && status != 302) // default redirect statuses 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 List(); /// /// Gets or sets a dictionary of Headers to append to the Response object. /// public Dictionary Headers { get; set; } = new Dictionary(); #endregion } }