Source included BasePage for EPiServer templates. Supports encoding settings and language translation.

Namespace:  EPiServer
Assembly:  EPiServer (in EPiServer.dll) Version: 6.0.530.0

Syntax

C#
public abstract class PageBase : Page, 
	IPageSource, ICurrentPage

Remarks

This class inherits from Page, which has the core functionality for setting up and rendering a standard Web form (.aspx file.) The PageBase class extends the functionality of the Page class with EPiServer-specific features as access to configuration settings (Configuration property), user information (CurrentUser property) and information about the current EPiServer page (CurrentPage property.)

The PageBase class is an abstract class, meaning it cannot be instantiated. This class is the base class of all template classes in EPiServer. TemplatePage is the most used class as base for Web forms.

PageBase also implements the IPageSource interface, which enables you to retrieve other EPiServer pages easily.

Note: If you use anything apart from templates that inherit from PageBase to create EPiServer page types, no dynamic content will be presented on the Web page.
CopyC#
#region Copyright � 1997-2008 EPiServer AB. All Rights Reserved.
/*
This code may only be used according to the EPiServer License Agreement.
The use of this code outside the EPiServer environment, in whole or in
parts, is forbidden without prior written permission from EPiServer AB.

EPiServer is a registered trademark of EPiServer AB. For more information 
see http://www.episerver.com/license or request a copy of the EPiServer 
License Agreement by sending an email to info@episerver.com*/

#endregion

using System;
using System.Web;
using System.Web.Security;
using System.Web.UI;

using EPiServer.ClientScript;
using EPiServer.Configuration;
using EPiServer.Core;
using EPiServer.DataAbstraction;
using EPiServer.DataAccess;
using EPiServer.Diagnostics;
using EPiServer.Globalization;
using EPiServer.Security;
using EPiServer.Web;
using EPiServer.Web.PageExtensions;

using log4net;

using ScriptManager = EPiServer.ClientScript.ScriptManager;

namespace EPiServer
{
    /// <summary>
    /// Define the type/level of translation to be done by the system.
    /// </summary>
    public enum TranslateType
    {
        /// <summary>
        /// No translation of controls.
        /// </summary>
        None,
        /// <summary>
        /// Only do translation for controls that implement ITranslate
        /// </summary>
        OnlyExplicitControls,
        /// <summary>
        /// Translate all controls that implement ITranslate or has a property named "translate"
        /// </summary>
        AllControls
    }

    /// <summary>
    /// The delegate type used by <see cref="EPiServer.PageBase.AccessDenied"/> to handle access denied reporting to a web browser.
    /// </summary>
    /// <param name="source">The object that invokes the delegate.</param>
    public delegate void AccessDeniedDelegate(object source);

    /// <summary>
    /// The delegate type used to generate PageSetup event from the <see cref="PageBase"/> class.
    /// </summary>
    public delegate void PageSetupEventHandler(PageBase sender, PageSetupEventArgs e);

    /// <summary>
    /// <img src="Icons/CSharp.gif" alt="Source included" border="0" /> 
    /// BasePage for EPiServer templates. Supports encoding settings and language translation.
    /// </summary>
    /// <remarks>
    /// <para>
    /// This class inherits from <see cref="System.Web.UI.Page"/>, which has the core functionality for 
    /// setting up and rendering a standard Web form (.aspx file.) The <b>PageBase</b> class extends the 
    /// functionality of the <b>Page</b> class with EPiServer-specific features as access to configuration 
    /// settings (<b>Configuration</b> property), user information (<b>CurrentUser</b> property) and 
    /// information about the current EPiServer page (<b>CurrentPage</b> property.)
    /// </para>
    /// The <b>PageBase</b> class is an abstract class, meaning it cannot be instantiated. This class is 
    /// the base class of all template classes in EPiServer. <b>TemplatePage</b> is the most used class as base 
    /// for Web forms.
    /// <para>
    /// <b>PageBase</b> also implements the <b>IPageSource</b> interface, which enables you to retrieve other 
    /// EPiServer pages easily. 
    /// </para>
    /// <note>
    /// If you use anything apart from templates that inherit from <b>PageBase</b> to create EPiServer 
    /// page types, no dynamic content will be presented on the Web page.
    /// </note>
    /// <code source="../EPiServerNET/PageBase.cs" lang="cs"/>
    /// </remarks>
    /// <examples>
    /// The following code example demonstrates the usage of <b>PageBase</b>.
    /// <code source="../CodeSamples/EPiServer/PageBaseSamples.cs" region="PageBase" lang="cs" />
    /// </examples>
    public abstract class PageBase : System.Web.UI.Page, IPageSource, ICurrentPage
    {
        private AccessDeniedDelegate _accessDenied;
        private PageReference _currentPageLink;
        private bool _isDefaultPageLink;
        private ContextMenu _menu;
        private PageTranslation _pageTranslation;
        private int _options;
        private ICurrentPage _currentPageHandler;
        private ISavePage _savePageHandler;
        private ScriptManager _scriptManager;
        private bool _pageDataPrepared;

        protected System.Web.UI.HtmlControls.HtmlInputHidden EPCurrentPageLink;

        private static HttpCacheability _httpCacheability = EPiServer.Configuration.Settings.Instance.HttpCacheability;
        private static PageSetupEventHandler _pageSetup;
        private static readonly ILog log = LogManager.GetLogger(typeof(PageBase));

        /// <summary>
        /// Initializes a new instance of the <see cref="PageBase"/> class.
        /// </summary>
        /// <param name="options">The page options to enable.</param>
        /// <remarks>
        /// The options parameter is a bitmap constructed from the OptionFlag of Page plugin classes from
        /// the EPiServer.Web.PageExtensions namespace.
        /// </remarks>
        public PageBase(int options)
        {
            _options = options;
            OnPageSetup();
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="PageBase"/> class.
        /// </summary>
        /// <param name="enable">The page options to enable.</param>
        /// <param name="disable">The page options to disable.</param>
        /// <remarks>
        /// The disable bitmap will override enabled options in case of conflicting options.
        /// </remarks>
        public PageBase(int enable, int disable)
            : this((
            PageTranslation.OptionFlag |
            CultureSupport.OptionFlag |
            LoadCurrentPage.OptionFlag |
            ServerTransferBugfix.OptionFlag |
            enable) & (~disable))
        {
        }

        #region Page Extension handling

        /// <summary>
        /// Gets the page extension options, which is a bitmap constructed from the OptionFlag of Page plugin classes from
        /// the EPiServer.Web.PageExtensions namespace.
        /// </summary>
        public int Options
        {
            get { return _options; }
        }

        /// <summary>
        /// Determines whether the specific page extension option is enabled.
        /// </summary>
        /// <param name="option">The option to check.</param>
        /// <returns>
        ///     <c>true</c> if option is an enabled option; otherwise, <c>false</c>.
        /// </returns>
        /// <remarks>
        /// option is the OptionFlag value for one of the page extensions defined with the [PagePlugIn] attribute.
        /// </remarks>
        public bool IsOptionEnabled(int option)
        {
            return (Options & option) == option;
        }

        /// <summary>
        /// Exposes the PageSetup event which is raised when a new page instance is created.
        /// </summary>
        /// <remarks>
        /// Attach to this event to be able to attach your code to specific events in the page lifecycle, allowing
        /// for your page extension to extend any page that derives from PageBase with your custom code.
        /// </remarks>
        public static event PageSetupEventHandler PageSetup
        {
            add { _pageSetup += value; }
            remove { _pageSetup -= value; }
        }

        /// <summary>
        /// Called when a new page instance is created.
        /// </summary>
        /// <remarks>
        /// Will raise the PageSetup event to allow page extensions to hook into the page lifecycle.
        /// </remarks>
        private void OnPageSetup()
        {
            if (_pageSetup != null)
            {
                PageSetupEventArgs e = new PageSetupEventArgs(Options);
                _pageSetup(this, e);
            }
        }

        #endregion

        /// <summary>
        /// Gets or sets the current page handler.
        /// </summary>
        /// <value>The current page handler.</value>
        /// <remarks>
        /// The page handler is used to retrieve the current page. By assignig your own implementation to this property
        /// you can take control over the process of retrieving the current page.
        /// </remarks>
        public ICurrentPage CurrentPageHandler
        {
            get
            {
                if (_currentPageHandler == null)
                {
                    _currentPageHandler = new LoadEmptyCurrentPage();
                }
                return _currentPageHandler;
            }
            set { _currentPageHandler = value; }
        }

        /// <summary>
        /// Gets or sets the save page handler.
        /// </summary>
        /// <value>The save page handler.</value>
        /// <remarks>
        /// The save page handler is responsible for saving the current page. You may create your own implementation of 
        /// ISavePage if you have very specific needs, but this is not recommended as it is a very complex task.
        /// </remarks>
        public ISavePage SavePageHandler
        {
            get { return _savePageHandler; }
            set { _savePageHandler = value; }
        }

        /// <summary>
        /// Raises the <see cref="E:System.Web.UI.Page.PreRenderComplete"/> event after the <see cref="M:System.Web.UI.Page.OnPreRenderComplete(System.EventArgs)"/> event and before the page is rendered.
        /// </summary>
        /// <param name="e">An <see cref="T:System.EventArgs"/> that contains the event data.</param>
        /// <remarks>
        /// We override this method to make sure that EPiServers script manager can render the script blocks needed.
        /// </remarks>
        protected override void OnPreRenderComplete(EventArgs e)
        {
            base.OnPreRenderComplete(e);

            if (_scriptManager != null)
            {
                _scriptManager.RegisterClientScriptBlocks(this);
            }
        }

        #region Translation support

        /// <summary>
        /// Translate the given string to the current language.
        /// </summary>
        /// <param name="key">A string to translate</param>
        /// <returns>The translated string.</returns>
        /// <remarks>
        /// <see cref="EPiServer.Core.LanguageManager.Translate"/> for more information on 
        /// the format of the string to translate.
        /// </remarks>
        public string Translate(string key)
        {
            return LanguageManager.Instance.Translate(key);
        }

        /// <summary>
        /// Translate the given string to the current language, in a script-safe format.
        /// </summary>
        /// <param name="key">A string to translate</param>
        /// <returns>The translated string.</returns>
        /// <remarks>
        /// <see cref="EPiServer.Core.LanguageManager.TranslateForScript"/> for more information on 
        /// the format of the string to translate.
        /// </remarks>
        public string TranslateForScript(string key)
        {
            return LanguageManager.Instance.TranslateForScript(key);
        }

        /// <summary>
        /// Select the type of automatic translation of controls that will take place.
        /// </summary>
        /// <remarks>
        /// <para>
        /// EPiServer will try to automatically translate texts of Web controls using the built-in 
        /// translation system. By default this property is initialized to <b>TranslateType.AllControls</b>. 
        /// If you know that there are no controls that contain text to be translated, set <b>AutomaticTranslation</b> 
        /// to <b>None</b> to improve performance.
        /// </para>
        /// <para>
        /// See <see cref="EPiServer.TranslateType"/> for more information about the behavior of automatic translation.
        /// </para>
        /// </remarks>
        [Obsolete("Use EPiServer.PageBase.PageTranslation.TranslateType instead of EPiServer.PageBase.AutomaticTranslation", false)]
        public TranslateType AutomaticTranslation
        {
            get { return PageTranslation.TranslateType; }
            set { PageTranslation.TranslateType = value; }
        }

        /// <summary>
        /// Gets or sets the page translation object.
        /// </summary>
        /// <value>The page translation.</value>
        public PageTranslation PageTranslation
        {
            get { return _pageTranslation; }
            set { _pageTranslation = value; }
        }

        #endregion

        /// <summary>
        /// Determine if the current page request was done without an "id=..." parameter.
        /// </summary>
        /// <remarks>
        /// The system will handle a missing id parameter by using the <b>pageStartId</b> value
        /// from <see cref="Configuration"/>.
        /// </remarks>
        public bool IsDefaultPageLink
        {
            get
            {
                // This statement is present to make sure that CurrentPageLink is parsed, since _isDefaultPageLink
                // is set by CurrentPageLink code.
                if (!PageReference.IsValue(CurrentPageLink))
                {
                    return false;
                }
                return _isDefaultPageLink;
            }
            set { _isDefaultPageLink = value; }
        }

        /// <summary>
        /// Gets a value indicating whether this instance is creating a new page in EPiServer.
        /// </summary>
        /// <value>
        ///     <c>true</c> if this instance is creating a new page; otherwise, <c>false</c>.
        /// </value>
        public bool IsNewPage
        {
            get { return SavePageHandler.IsNewPage; }
        }

        /// <summary>
        /// Gets the page that a newly created page will be placed below.
        /// </summary>
        public PageReference NewPageParent
        {
            get { return SavePageHandler.NewPageParent; }
        }

        /// <summary>
        /// Prepares the data for the page to be saved.
        /// </summary>
        /// <remarks>
        /// Moves new data from web controls into the property objects or CurrentPage and also triggers the global validators.
        /// </remarks>
        protected virtual void PrepareForSave()
        {
            // Allow us to be called multiple times, but only do anything once. There's also no need to validate a read-only page.
            if (_pageDataPrepared || CurrentPage == null || CurrentPage.IsReadOnly || SavePageHandler == null || SavePageHandler.RequestedSaveAction == SaveAction.None)
            {
                return;
            }
            _pageDataPrepared = true;

            SetValuesForPropertyControls(this);
            GlobalPageValidation.OnValidate(CurrentPage);
        }

        /// <summary>
        /// Get new data from property web controls into the actual property objects for all controls implementing IPropertyControl.
        /// </summary>
        /// <remarks>Used to update PagaData values when saving a page.</remarks>
        public virtual void SetValuesForPropertyControls(Control control)
        {
            foreach (Control childControl in control.Controls)
            {
                if (childControl is IPropertyControl && ((IPropertyControl)childControl).Enabled)
                {
                    ((IPropertyControl)childControl).ApplyChanges();
                }
                SetValuesForPropertyControls(childControl);
            }
        }

        /// <summary>
        /// Perfoms validation logic and then saves the current page.
        /// </summary>
        /// <remarks>
        /// This performs validation logic, and a save. If you must save without validation, use the SavePageHandler directly.
        /// </remarks>
        public virtual bool SavePage()
        {
            PrepareForSave();

            if (!IsValid)
            {
                return false;
            }

            // Insert the value for the Page folder 
            if (ViewState["PageFolderID"] != null && IsNewPage)
            {
                CurrentPage["PageFolderID"] = ViewState["PageFolderID"];
            }

            // Handle the case where we are publishing a version that is set as delayed publish.
            PageVersion version = DataFactory.Instance.LoadVersion(CurrentPage.PageLink);
            if (SavePageHandler.RequestedSaveAction == SaveAction.Publish && version != null && version.Status == VersionStatus.DelayedPublish)
            {
                CurrentPage["PageStartPublish"] = DateTime.Now;
                // When PageStartPublish is changed, the page must be published with ForceCurrentVersion to act correctly in all situations later in the process.
                SavePageHandler.RequestedSaveAction = SaveAction.Publish | SaveAction.ForceCurrentVersion;
            }

            // Check if we should save the page (e.g if the properties has been modified and etc)
            if (ShouldSave)
            {
                SavePageHandler.SavePage();
            }

            if (IsNewPage && ViewState["PageFolderID"] == null)
            {
                ViewState["PageFolderID"] = CurrentPage["PageFolderID"];
            }
            return true;
        }

        /// <summary>
        /// Gets a value indicating whether we should save this page.
        /// </summary>
        /// <value><c>true</c> if we should save this page; otherwise, <c>false</c>.</value>
        private bool ShouldSave
        {
            get
            {
                if (SavePageHandler.IgnoreModifiedOnSave)
                {
                    return true;
                }

                if (CurrentPage.IsModified)
                {
                    return true;
                }

                return (SavePageHandler.RequestedSaveAction == SaveAction.CheckIn || SavePageHandler.RequestedSaveAction == SaveAction.Publish) && CurrentPageLink.WorkID != 0;
            }
        }

        #region Access control

        /// <summary>
        /// Determine the access level that is required for the current web request.
        /// </summary>
        /// <returns>The resulting access level.</returns>
        /// <remarks>This is the default implementation that simply requests Read access, regardless of the type of requested operation.</remarks>
        public virtual AccessLevel RequiredAccess()
        {
            return AccessLevel.Read;
        }

        /// <summary>
        /// The access level that the current user holds to the current page.
        /// </summary>
        /// <returns>The access level.</returns>
        /// <remarks>
        /// Will return the actual access level for the current page, or Read if there
        /// is no current page.
        /// </remarks>
        public virtual AccessLevel QueryAccess()
        {
            if (CurrentPage != null)
            {
                return CurrentPage.QueryAccess();
            }
            return AccessLevel.Read;
        }

        /// <summary>
        /// Verify that access control requirements are met for the current web request.
        /// </summary>
        /// <returns>True if access requirements are met.</returns>
        /// <remarks>
        /// This method simply checks that all bits set in <see cref="RequiredAccess"/> 
        /// are set in <see cref="QueryAccess"/>.
        /// </remarks>
        public bool QueryDistinctAccess()
        {
            AccessLevel required = RequiredAccess();
            return (required == AccessLevel.NoAccess) || ((QueryAccess() & required) == required);
        }

        /// <summary>
        /// Verify that access control requirements are met for the current web request.
        /// </summary>
        /// <remarks>
        /// This method is called as part of the PageBase.OnInit method. It determines the current user's 
        /// access level and compares it to the current page. If the user does not have sufficient access, 
        /// the PageBase.AccessDenied method will be called.
        /// <note>
        /// Will trigger an AccessDenied if the access requirements are not met.
        /// </note>
        /// </remarks>
        public void CheckAccess()
        {
            if (!QueryDistinctAccess())
            {
                AccessDenied();
            }
        }


        /// <summary>
        /// Makes sure that the right page template is used to present a page.
        /// </summary>
        /// <remarks>
        /// This method is called as part of the page load process. If a page (as determined by the ID query 
        /// string parameter on the URL) is accessed with an .aspx file that is not the correct page template 
        /// file, this method will throw an exception of type <b>EPiServerException</b>. If you have a page type that 
        /// should be displayed using different page templates, you should override this method and do your 
        /// custom validation. A validation error should be signalled by throwing an <b>EPiServerException</b>.
        /// </remarks>
        public virtual void ValidatePageTemplate()
        {
            // A couple of early esits, we must have a current page that is not remote, 
            // validation has to be enabled and we don't do validation things in edit/admin.
            if (CurrentPage == null || 
                CurrentPageLink.IsRemote() || 
                IsOptionEnabled(CustomPageLink.OptionFlag) || 
                !Settings.Instance.PageValidateTemplate || 
                HttpRequestSupport.IsRequestSystemDirectory)
            {
                return;
            }

            // Do actual validation by comparing the template file name of the page type with the Path of the incoming request.
            PageType pageType = PageType.Load(CurrentPage.PageTypeID);
            if (pageType == null || String.Equals(pageType.FileName, Request.Path, StringComparison.OrdinalIgnoreCase))
            {
                return;
            }

            // validation failed, log warning and throw exception
            log.Warn("1.1.2 Loading page with wrong page template");
            if (CurrentPage.QueryAccess() > AccessLevel.Read)
            {
                throw new EPiServerException(String.Format("The current template \"{0}\" does not match the specified page type file \"{1}\".", Request.Path, pageType.FileName));
            }
            throw new EPiServerException("The current template does not match the specified page type file.");
        }

        /// <summary>
        /// The method that is invoked when the current users access rights are not suffcient for the
        /// operation that he tries to perform.
        /// </summary>
        /// <remarks>
        /// This method will perform different functions depending on the authentication method in use. If
        /// forms authentication is used, then it will redirect the user to the login page as defined in web.config.
        /// If windows authentication is used, an Access Denied header (http status 401) will be sent to the client.
        /// </remarks>
        /// <example>
        /// The following code example demonstrates the usage of <b>AccessDenied</b> to deny access to a user that is not 
        /// logged on. The example is taken from the Web user control LoginStatus.ascx, which is delivered with 
        /// EPiServer. It must be understood that the click event for ASP.NET Server control Login has been linked 
        /// to the function Login_Click, so that when the user clicks the Login button, this function is executed. 
        /// Note that the call to AccessDenied does not necessarily mean that the user is denied access - it can also 
        /// be used to present the user with a login dialog. This happens in Web user control QuickBar.ascx.
        /// <code source="../CodeSamples/EPiServer/PageBaseSamples.cs" region="AccessDenied" lang="cs"/>
        /// </example>
        public virtual void AccessDenied()
        {
            log.Warn("1.1.1 Access denied");

            if (_accessDenied == null)
            {
                if (EPiServer.Security.FormsSettings.IsFormsAuthentication)
                {
                    _accessDenied = new AccessDeniedDelegate(FormLogonAccessDenied);
                }
                else
                {
                    _accessDenied = new AccessDeniedDelegate(BrowserLogonAccessDenied);
                }
            }

            _accessDenied(this);
        }

        /// <summary>
        /// Sends an access denied message when using bowser-based authentication (Basic Auth or NTLM).
        /// </summary>
        /// <param name="sender">The sender.</param>
        private static void BrowserLogonAccessDenied(object sender)
        {
            HttpContext.Current.Response.Clear();
            HttpContext.Current.Response.Status = "401 Unauthorized";
            HttpContext.Current.Response.Write("Access denied.");
            HttpContext.Current.Response.Flush();
            HttpContext.Current.Response.End();
        }

        /// <summary>
        /// Handles access denied for forms authentication scenarios by redirecting to the logon form.
        /// </summary>
        /// <param name="sender">The sender.</param>
        private void FormLogonAccessDenied(object sender)
        {
            string redirectUrl = Request.Url.PathAndQuery;

            if (UrlRewriteProvider.IsFurlEnabled)
            {
                UrlBuilder url = new UrlBuilder(redirectUrl);
                Global.UrlRewriteProvider.ConvertToExternal(url, null, Response.HeaderEncoding);
                redirectUrl = (string)url;
            }
            Response.Redirect(FormsAuthentication.LoginUrl + "?ReturnUrl=" + Server.UrlEncode(redirectUrl), true);
        }
        #endregion

        /// <summary>
        /// Gets or sets the context menu for this page.
        /// </summary>
        /// <value>The context menu.</value>
        /// <remarks>
        /// To enable the context menu you should make sure that ContextMenu.OptionFlag (in namespace EPiServer.Web.PageExtensions) is set for the
        /// PageBase constructor parameter.
        /// </remarks>
        public ContextMenu ContextMenu
        {
            get { return _menu; }
            set { _menu = value; }
        }

        /// <summary>
        /// Get page data for current page.
        /// </summary>
        /// <value>
        /// A <see cref="EPiServer.Core.PageData"/> object, null if failure.
        /// </value>
        /// <remarks>
        /// <para>
        /// Abstract property, must be implemented in derived classes.
        /// </para>
        /// <para>
        /// A little caution is necessary when using <b>CurrentPage</b> in the HTML part of Web forms, as 
        /// <b>CurrentPage</b> is implemented from <b>IPageSource</b> and <see cref="EPiServer.UserControlBase"/> 
        /// also implements <b>IPageSource</b>. 
        /// This means that <b>CurrentPage</b> will have different meanings in the same Web form depending on 
        /// whether it's used outside any included EPiServer Web custom control in general and outside 
        /// any templated EPiServer Web custom control in particular.
        /// </para>
        /// </remarks>
        /// <example>
        /// The following code example demonstrates the usage of both <b>CurrentPage</b> and <b>Configuration</b>. 
        /// The example is used in EPiServer's sample Web site to send e-mail. <b>Configuration</b> is used 
        /// to initalize the mail server and also, together with <b>CurrentPage</b>, to send information to 
        /// the recipient of the e-mail.
        /// <code source="../CodeSamples/EPiServer/PageBaseSamples.cs" region="ConfigurationSendEmail" lang="cs" />
        /// The following example demonstrates the usage of <b>CurrentPage</b> on a page template file, outside of any 
        /// included controls.
        /// <code source="../CodeSamples/EPiServer/PageBaseSamples.aspx" region ="ConfigurationPageTemplate" lang="aspnet" />
        /// The following example demonstrates the usage of <b>CurrentPage</b> on a page template file, 
        /// inside ContentFramework and Content controls.
        /// <code source="../CodeSamples/EPiServer/PageBaseSamples.aspx" region ="ConfigurationContentFramework" lang="aspnet" />
        /// The following example demonstrates the usage of <b>CurrentPage</b> inside an EPiServer templated control.
        /// <code source="../CodeSamples/EPiServer/PageBaseSamples.aspx" region ="ConfigurationTemplatedControl" lang="aspnet" />
        /// The following code example demonstrates the usage of <b>CurrentPage</b> and <b>IsValue</b>. 
        /// <b>CurrentPage</b> is used to read the content of two properties; NewsCount and MainImage. 
        /// To safeguard the property access, the presence and possible contents of both properties are 
        /// first tested using the <b>IsValue</b> method.
        /// <code source="../CodeSamples/EPiServer/PageBaseSamples.cs" region="IsValue" lang="cs" />
        /// </example>
        [System.ComponentModel.Browsable(false)]
        [System.ComponentModel.DesignerSerializationVisibility(System.ComponentModel.DesignerSerializationVisibility.Hidden)]
        public virtual PageData CurrentPage
        {
            get { return CurrentPageHandler.CurrentPage; }
            set { CurrentPageHandler.CurrentPage = value; }
        }

        /// <summary>
        /// Get the reference to the current page.
        /// </summary>
        /// <value>A PageReference to the current page.</value>
        /// <remarks>
        /// <para>
        /// Parses the query string / form parameters to get the page reference.
        /// </para>
        /// <b>CurrentPageLink</b> has no place inside templated controls, since the <b>Container</b> 
        /// property is of the type <b>PageTemplateContainer</b>, which doesn't implement <b>CurrentPageLink</b>.
        /// </remarks>
        /// <example>
        /// The following code example demonstrates the usage of <b>CurrentPageLink</b> to 
        /// retrieve the <b>PageData</b> object.
        /// <code source="../CodeSamples/EPiServer/PageBaseSamples.cs" region="CurrentPageLink" lang="cs" />
        /// </example>
        [System.ComponentModel.Browsable(false)]
        [System.ComponentModel.DesignerSerializationVisibility(System.ComponentModel.DesignerSerializationVisibility.Hidden)]
        public virtual PageReference CurrentPageLink
        {
            get
            {
                if (!PageReference.IsNullOrEmpty(_currentPageLink))
                {
                    return _currentPageLink;
                }

                string pageLinkString = GetPageLinkString();

                // Try to parse string into a page reference
                if (!PageReference.TryParse(pageLinkString, out _currentPageLink))
                {
                    // Parse failed, fall back to start page
                    _currentPageLink = PageReference.StartPage;
                    _isDefaultPageLink = true;

                    // Special handling of remote links - validate that remote site is defined ant that we are allowed to reference it
                    if (!PageReference.IsNullOrEmpty(_currentPageLink) && _currentPageLink.IsRemote())
                    {
                        RemoteSite site = RemoteSite.RemoteSites[_currentPageLink.RemoteSite];
                        if (site == null || !site.AllowUrlLookup)
                        {
                            _currentPageLink.RemoteSite = null;
                        }
                    }
                }
                return _currentPageLink;
            }
            set
            {
                _currentPageLink = value;
                if (EPCurrentPageLink != null)
                {
                    EPCurrentPageLink.Value = _currentPageLink.ToString();
                }
            }
        }

        private string GetPageLinkString()
        {
            string pageLink;

            // First check if we have some values defined in the EPCurrentPageLink field
            if (EPCurrentPageLink != null)
            {
                pageLink = EPCurrentPageLink.Value;
                if (!String.IsNullOrEmpty(pageLink))
                {
                    return pageLink;
                }

                // ...or if we got something in a postback to the EPCurrentPageLinkField. Note that we do this handling because
                // this routine might be called before ASP.NET postback handling has been done.
                pageLink = Request.Form[EPCurrentPageLink.UniqueID];
                if (!String.IsNullOrEmpty(pageLink))
                {
                    EPCurrentPageLink.Value = pageLink;
                    return pageLink;
                }
            }

            // If nothing from the EPCurrentPageLink field, check query string values

            // "custompageid" query string overrides the "id" query string if present.
            // This can be used to cross connect a page with another template.
            pageLink = Request.QueryString["custompageid"];
            if (IsOptionEnabled(CustomPageLink.OptionFlag) && !String.IsNullOrEmpty(pageLink))
            {
                return pageLink;
            }

            // This is the standard way of selecting the CurrentPageLink
            return Request.QueryString["id"];
        }

        /// <summary>
        /// Retrieves a PageData object based on supplied <see cref="EPiServer.Core.PageReference"/> 
        /// argument (from <see cref="EPiServer.Core.IPageSource"/>).
        /// </summary>
        /// <param name="pageLink"></param>
        /// <returns>Returns the PageData object for the specified EPiServer.Core.PageReference argument.</returns>
        /// <example>
        /// The following code example demonstrates the usage of <b>GetPage</b> to retrieve the <b>PageData</b> 
        /// object for the current page.
        /// <code>EPiServer PageData tmpPageData = GetPage( CurrentPageLink );</code>
        /// The following code example demonstrates the usage of <b>GetPage</b> and <b>CurrentPage</b> 
        /// to retrieve <b>PageData</b> information for the page specified in the <b>EventsContainer</b> attribute.
        /// <code source="../CodeSamples/EPiServer/PageBaseSamples.cs" region="GetPage" lang="cs" />
        /// </example>
        public virtual PageData GetPage(PageReference pageLink)
        {
            if (IsDesignMode)
            {
                return new PageData();
            }

            PageData page = DataFactory.Instance.GetPage(pageLink);

            // Check if the user has access to the page
            AccessLevel requiredAccess = (pageLink.ID == CurrentPageLink.ID) ? RequiredAccess() : AccessLevel.Read;
            if (!page.GetSecurityDescriptor().HasAccess(PrincipalInfo.CurrentPrincipal, requiredAccess))
            {
                // If the user is already logged in, throw exception
                if (PrincipalInfo.CurrentPrincipal.Identity.IsAuthenticated)
                {
                    throw new AccessDeniedException();
                }

                // If not logged in, make sure they get a graceful login screen
                AccessDenied();
            }

            return page;
        }

        /// <summary>
        /// Retrieves a <b>PageData</b> listing based on supplied <see cref="EPiServer.Core.PageReference"/> 
        /// argument (from <see cref="EPiServer.Core.IPageSource"/>).
        /// </summary>
        /// <param name="pageLink"></param>
        /// <returns></returns>
        /// <remarks>
        /// <para>
        /// The <b>IPageSource</b> interface is implemented by many classes, such as <see cref="EPiServer.PageBase"/> 
        /// (and its descendants), <see cref="EPiServer.DataFactory"/>, <see cref="EPiServer.Web.WebControls.PageControlBase"/>. 
        /// You typically use the members of this interface to get information about the current page 
        /// (the <b>CurrentPage</b> property) or use either the <b>GetPage</b> or <b>GetChildren</b> methods to 
        /// retrieve page data. 
        /// </para>
        /// <para>
        /// <b>IPageSource</b> was created as an interface due to the fact that the <b>GetPage</b> and 
        /// <b>GetChildren</b> methods normally have the same implementation independently of the class 
        /// that implements <b>IPageSource</b>. The <b>CurrentPage</b> property has different meanings 
        /// for different implementations. As an example, <b>CurrentPage</b> on the <b>PageBase</b> 
        /// class refers to the currently loaded page (based on the ID in the query string). 
        /// </para>
        /// <note>
        /// <b>Note:</b>  As <b>IPageSource</b> is an interface, it doesn't implement any of its own attributes or 
        /// methods itself. This means that you must be aware of which class implementing <b>IPageSource</b> 
        /// that you're dealing with. For example, PageBase.CurrentPage returns a 
        /// <see cref="EPiServer.Core.PageData"/> object for the current page, the same for <see cref="EPiServer.UserControlBase"/>. 
        /// However, in a templated control such as EPiServer.Web.WebControls.PageList, <b>CurrentPage</b>
        /// instead means the current page in the iteration.
        /// </note>
        /// </remarks>
        /// <example>
        /// The following code example demonstrates a specialized implementation of <b>IPageSource</b> to set 
        /// custom access level restrictions to the pages returned.
        /// <code source="../CodeSamples/EPiServer/PageBaseSamples.cs" region="SetRestriction" />
        /// </example>
        public virtual PageDataCollection GetChildren(PageReference pageLink)
        {
            if (IsDesignMode)
            {
                return new PageDataCollection();
            }

            return DataFactory.Instance.GetChildren(pageLink);
        }

        /// <summary>
        /// Gets a value indicating whether this instance is design mode.
        /// </summary>
        /// <value>
        ///     <c>true</c> if this instance is design mode; otherwise, <c>false</c>.
        /// </value>
        protected bool IsDesignMode
        {
            get { return Site != null && Site.DesignMode; }
        }

        /// <summary>
        /// Determine if the named property exists and has a value.
        /// </summary>
        /// <param name="propertyName">The name of the page property to check.</param>
        /// <returns>True if the property exists with a valid (i e non-null) value.</returns>
        /// <remarks>
        /// <b>IsValue</b> can be seen as a safe way to check whether a property exists and has been assigned contents.
        /// </remarks>
        /// <example>
        /// The following code example demonstrates the usage of <b>CurrentPage</b> and <b>IsValue</b>. <b>CurrentPage</b> 
        /// is used to read the content of two properties; NewsCount and MainImage. To safeguard the property access, 
        /// the presence and possible contents of both properties are first tested using the <b>IsValue</b> method.
        /// <code source="../CodeSamples/EPiServer/PageBaseSamples.cs" region="IsValue" />
        /// </example>
        [System.ComponentModel.Browsable(false)]
        [System.ComponentModel.DesignerSerializationVisibility(System.ComponentModel.DesignerSerializationVisibility.Hidden)]
        public bool IsValue(string propertyName)
        {
            PropertyData property = CurrentPage.Property[propertyName];
            return property != null && !property.IsNull;
        }

        /// <summary>
        /// Helper method to construct a URL with a page reference parameter
        /// </summary>
        /// <param name="url">The original URL</param>
        /// <param name="pageLink">Page that should be referenced by the URL</param>
        /// <returns>A new URL</returns>
        /// <remarks>
        /// This method is primarily intended to be used with a URL read from the <b>PageURL</b> property 
        /// of a <see cref="EPiServer.Core.PageData"/> class. In some special cases you may need to retarget 
        /// the URL to another page or another version of the same page.
        /// </remarks>
        public string BuildUrlWithPageReference(string url, PageReference pageLink)
        {
            return UriSupport.BuildUrlWithPageReference(url, pageLink);
        }

        /// <summary>
        /// Sets the cache policy for this request based on parameters as current user and parameters.
        /// </summary>
        /// <remarks>
        /// Override this method if you wish to customize the cache policy for a page.
        /// The output cache is turned on based on the following criteria:
        /// <para>
        /// 1. The <b>EPnCachePolicyTimeout</b> in web.config is > 0.
        /// </para>
        /// <para>
        /// 2. The current user must not be logged on, aka Anonymous.
        /// </para>
        /// <para>
        /// 3. The request must be a GET type request. Hence, Postbacks and form postings will not be cached.
        /// </para>
        /// <para>
        /// 4. The current page must be the published version (the WorkID is == 0).
        /// </para>
        /// The cache parameters are fetched from web.config, more specifically the <b>EPsCacheVaryByCustom</b> and 
        /// <B>EPsCacheVaryByParams</B> settings. Additionally, a dependency to the <b>DataFactoryCache</b> is set. 
        /// When pages are changed, the cache is flushed. Cache item expiration is set to the <b>HttpCacheExpiration</b> 
        /// setting, which is the number of seconds the item should reside in the cache, as long as the <b>StopPublish</b> 
        /// value of the page is not less than the policy timeout (in which case, the <b>StopPublish</b> value is used).
        /// </remarks>
        protected virtual void SetCachePolicy()
        {
            if (UseOutputCache(Context) && CurrentPageLink.WorkID <= 0)
            {
                string cacheVaryByCustom = EPiServer.Configuration.Settings.Instance.HttpCacheVaryByCustom;
                int cacheVaryByCustomLength = cacheVaryByCustom.Length;

                Response.Cache.SetVaryByCustom(cacheVaryByCustom);

                string[] cacheVaryByParams = EPiServer.Configuration.Settings.Instance.HttpCacheVaryByParams;
                foreach (string varyBy in cacheVaryByParams)
                {
                    Response.Cache.VaryByParams[varyBy] = true;
                }

                Response.Cache.SetValidUntilExpires(true);
                Response.AddCacheItemDependency(DataFactoryCache.VersionKey);
                Response.Cache.AddValidationCallback(new HttpCacheValidateHandler(ValidateOutputCache), null);
                DateTime cacheExpiration = DateTime.Now + EPiServer.Configuration.Settings.Instance.HttpCacheExpiration;
                DateTime stopPublish = CurrentPage != null ? CurrentPage.StopPublish : DateTime.MaxValue;
                Response.Cache.SetExpires(cacheExpiration < stopPublish ? cacheExpiration : stopPublish);
                Response.Cache.SetCacheability(_httpCacheability);
            }
            else
            {
                //We dont use HttpCacheability.NoCache since it is not supported by all browsers.
                //instead we set a historical expiration date.
                Response.Cache.SetExpires(DateTime.Now.AddDays(-1));
                Response.Cache.SetCacheability(HttpCacheability.Private);
            }
        }

        /// <summary>
        /// Decides if the output cache should be used.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <returns></returns>
        private static bool UseOutputCache(HttpContext context)
        {
            return !PrincipalInfo.CurrentPrincipal.Identity.IsAuthenticated && EPiServer.Configuration.Settings.Instance.HttpCacheExpiration != TimeSpan.Zero && String.Compare(context.Request.HttpMethod, "GET", true) == 0;
        }

        /// <summary>
        /// Validates the output cache.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="data">The data.</param>
        /// <param name="validationStatus">The validation status.</param>
        private static void ValidateOutputCache(HttpContext context, object data, ref HttpValidationStatus validationStatus)
        {
            KeepUserLoggedOn();
            if (UseOutputCache(context))
            {
                StatisticsLog.Log(data as StatisticsEventArgs);
                return;
            }

            validationStatus = HttpValidationStatus.IgnoreThisRequest;
        }

        /// <summary>
        /// Helper method to register a client script file. If the method is called multiple times using
        /// the same key, only a single instance of the file is registered.
        /// </summary>
        /// <param name="rootRelativePath">The script file that should be referenced, for instance "util/javascript/system.js"</param>
        /// <remarks>The relativePath will be used as the unique script key.</remarks>
        public void RegisterClientScriptFile(string rootRelativePath)
        {
            ClientScriptUtility.RegisterClientScriptFile(this, rootRelativePath);
        }

        /// <summary>
        /// Resolves the path relative the UI directory.
        /// </summary>
        /// <param name="path">The path.</param>
        /// <returns></returns>
        public string ResolveUrlFromUI(string path)
        {
            return UriSupport.ResolveUrlFromUIBySettings(path);
        }

        /// <summary>
        /// Resolves the path relative the Util directory.
        /// </summary>
        /// <param name="path">The path.</param>
        /// <returns></returns>
        public string ResolveUrlFromUtil(string path)
        {
            return UriSupport.ResolveUrlFromUtilBySettings(path);
        }

        #region Accessors

        /// <summary>
        /// Gets the script manager.
        /// </summary>
        /// <value>The script manager.</value>
        public ScriptManager ScriptManager
        {
            get
            {
                if (_scriptManager == null)
                {
                    _scriptManager = new ScriptManager();
                }
                return _scriptManager;
            }
        }

        #endregion

        /// <summary>
        /// Sets the <see cref="P:System.Web.UI.Page.Culture"/> and <see cref="P:System.Web.UI.Page.UICulture"/> for the current thread of the page.
        /// </summary>
        protected override void InitializeCulture()
        {
            if (IsOptionEnabled(CultureSupport.OptionFlag))
            {
                SystemLanguage.Instance.SetCulture();
                UserInterfaceLanguage.Instance.SetCulture();
            }
        }

        #region Web Form Designer generated code
        /// <summary>
        /// Raises the <see cref="E:System.Web.UI.Control.Init"/> event to initialize the page.
        /// </summary>
        /// <param name="e">An <see cref="T:System.EventArgs"/> that contains the event data.</param>
        override protected void OnInit(EventArgs e)
        {
            if (PageReference.IsNullOrEmpty(PageReference.StartPage))
            {
                string setupRedirect = ResolveUrlFromUI("admin/setup.aspx");
                if (string.Compare(new Uri(Request.Url, setupRedirect).AbsolutePath, Request.Url.AbsolutePath, StringComparison.OrdinalIgnoreCase) != 0 &&
                    string.Compare(new Uri(Request.Url, FormsAuthentication.LoginUrl).AbsolutePath, Request.Url.AbsolutePath, StringComparison.OrdinalIgnoreCase) != 0)
                {
                    //We need to use the Configuration.Settings.Instance.UIUrl in case UIUrl uses https and SiteUrl uses regular http, or vice versa.
                    Response.Redirect(UriSupport.AbsoluteUrlFromUIBySettings("admin/setup.aspx"));
                }
            }

            // 
            // CODEGEN: This call is required by the ASP.NET Web Form Designer.
            // 
            InitializeComponent();
            base.OnInit(e);

            KeepUserLoggedOn();
            SetCachePolicy();
            CheckAccess();
            ValidatePageTemplate();

            log.Info("1.1.3 Page initialized");
        }

        /// <summary>
        /// Makes sure windows authentication stay persistent even on anonymous pages when user has been logged in
        /// </summary>
        private static void KeepUserLoggedOn()
        {
            if (!EPiServer.Security.FormsSettings.IsFormsAuthentication && EPiServer.Configuration.Settings.Instance.UIKeepUserLoggedOn)
            {
                if (HttpContext.Current.Request.Cookies["KeepLoggedOnUser"] != null)
                {
                    if (!PrincipalInfo.CurrentPrincipal.Identity.IsAuthenticated)
                    {
                        BrowserLogonAccessDenied(null);
                    }
                }
                else if (PrincipalInfo.CurrentPrincipal.Identity.IsAuthenticated)
                {
                    HttpCookie keepLoggedOn = new HttpCookie("KeepLoggedOnUser", "True");
                    keepLoggedOn.Path = HttpContext.Current.Request.ApplicationPath;
                    HttpContext.Current.Response.Cookies.Add(keepLoggedOn);
                }
            }
        }

        /// <summary>
        /// Initializes the component.
        /// </summary>
        private void InitializeComponent()
        {
        }
        #endregion
    }
}

Inheritance Hierarchy

System..::.Object
  System.Web.UI..::.Control
    System.Web.UI..::.TemplateControl
      System.Web.UI..::.Page
        EPiServer..::.PageBase
          EPiServer..::.SimplePage

See Also