Source included Base class for global.asax that supports EPiServer-specific features.

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

Syntax

C#
public class Global : SiteBase

Remarks

The global.asax.cs file on an EPiServer site should inherit from Global. This is a requirement for the site to work.

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.Diagnostics;
using System.IO;
using System.Net;
using System.Web;

using EPiServer.Configuration;
using EPiServer.Core;
using EPiServer.Security;
using EPiServer.Web;
using EPiServer.Web.Hosting;

using log4net;

namespace EPiServer
{
    /// <summary>
    /// <img src="Icons/CSharp.gif" alt="Source included" /> 
    /// Base class for global.asax that supports EPiServer-specific features.
    /// </summary>
    /// <remarks>
    /// <para>
    /// The global.asax.cs file on an EPiServer site should inherit from <see cref="EPiServer.Global" />. 
    /// This is a requirement for the site to work.
    /// </para>
    /// <code source="../EPiServerNET/GlobalBase.cs" lang="cs"/>
    /// </remarks>
    /// <example>
    /// The following code example demonstrates the usage of <b>Global</b> to read the BaseDiretory.
    /// <code>
    /// BaseDirectory:
    /// <![CDATA[<%= EPiServer.Global.BaseDirectory %>]]>
    /// </code>
    /// </example>
    public partial class Global : SiteBase
    {
        // If the custom string that specifies which cached response is used to respond
        // to the current request is equal with VaryByCustomKey then the requsets path will be returned.
        private const string VaryByCustomKey = "path";

        // Base folder for the application - used to locate related files.
        private static string _baseDirectory;

        // The Application ID of the IIS instance that we are running under
        private static string _instanceName;

        // Resolves a host name or IP address to an System.Net.IPHostEntry instance
        private static IPHostEntry _localHost;

        // The current provider for Url rewrite functionality
        private static UrlRewriteProvider _urlRewriteProvider;

        // The static logger object 
        private static readonly ILog _log = LogManager.GetLogger(typeof(Global));

        /// <summary>
        /// Initializes a new instance of the <see cref="Global"/> class.
        /// </summary>
        public Global()
        {
            _log.Debug("EPiServer.Global ctor");
        }

        #region Static properties

        /// <summary>
        /// Global instance of language information
        /// </summary>
        /// <remarks>
        /// <para>
        /// The main use for the <b>EPLang</b> property is to translate EPiServer strings into the 
        /// current language. <b>EPLang</b> is an instantce of <see cref="EPiServer.Core.LanguageManager"/>, 
        /// which has a property, <b>Directory</b>, and a few methods. It is the Translate method that is
        /// primarily used.
        /// </para>
        /// By default, EPiServer will monitor the <b>BaseDirectory</b> + "/lang" directory for language files.
        /// <para>
        /// <b>Translate</b> has two overloaded variants. The first one is used in the first two code examples below. 
        /// It takes a string argument, key, in a simplified XML XPath form and returns the proper language string. 
        /// The key is typically something like "/admin/settings/heading", where the expression closely follows 
        /// the folder/ file/ usage pattern. You can also enter "#" to automatically construct a path to the current 
        /// file. For example, calling the <b>Translate</b> method in the file /templates/mypage.aspx with the key set to 
        /// '#heading', Translate( "#heading" ) is equivalent to Translate( "/templates/mypage/heading").
        /// </para>
        /// <para>
        /// <note>
        /// <b>Note:</b>  If the key does not begin with a "/" or "#", the key itself is simply returned as the result. 
        /// The reason for this behavior is to be able to use EPiServer Web controls that use Translate for visible 
        /// strings, but you might not have translations in place or you prefer not to translate the text.
        /// </note>
        /// </para>
        /// <para>
        /// The second form of <b>Translate</b> takes two string arguments, the first again being a key used in the 
        /// same way as earlier, and the second argument being a language identifier.
        /// </para>
        /// <para>
        /// </para>
        /// </remarks>
        [Obsolete("Use LanguageManager.Instance instead.")]
        public static LanguageManager EPLang
        {
            get { return LanguageManager.Instance; }
            set { }
        }

        /// <summary>
        /// Global instance of data factory
        /// </summary>
        [Obsolete("Use DataFactory.Instance instead.")]
        public static DataFactory EPDataFactory
        {
            get { return DataFactory.Instance; }
        }

        /// <summary>
        /// Gets or sets the URL rewrite provider.
        /// </summary>
        /// <value>
        /// Instance of the URLRewriteProvider <see cref="T:EPiServer.Web.UrlRewriteProvider"/> Type
        /// </value>
        public static UrlRewriteProvider UrlRewriteProvider
        {
            get
            {
                // Creates instance of UrlRewriteProvider on demand. 
                // Note! We do not protect against multiple treads creating a new UrlRewriteProvider. This is because if
                // multiple instances are created, it does not really matter. Reusing the same instance is simply a performance/
                // resource optimization. Protecting this with a lock would incur unecessary lock contention and performance
                // degradation.
                if (_urlRewriteProvider == null)
                {
                    // Get settings and initalize the url rewrite provider
                    UrlRewriteElement rewriteConfig = EPiServerSection.Instance.UrlRewriteSettings;
                    _urlRewriteProvider = UrlRewriteProvider.CreateInstance(rewriteConfig.Providers[rewriteConfig.DefaultProvider]);
                }
                return _urlRewriteProvider;
            }
            set { _urlRewriteProvider = value; }
        }

        /// <summary>
        /// The physical root directory where the site is installed, e.g. "D:/INetpub/WWWRoot/Example/", 
        /// when using Internet Information Services (IIS) folder. 
        /// </summary>
        /// <remarks>
        /// <b>Note:</b>  Slashes are used instead of backslashes.
        /// </remarks>
        public static string BaseDirectory
        {
            get
            {
                if (_baseDirectory == null)
                {
                    _baseDirectory = AppDomain.CurrentDomain.BaseDirectory;

                    // Make sure that base directory has a trailing backslash since existing code assumes this.
                    if (!_baseDirectory.EndsWith("/") && !_baseDirectory.EndsWith("\\"))
                    {
                        _baseDirectory += "\\";
                    }
                }
                return _baseDirectory;
            }
            set { _baseDirectory = value; }
        }

        /// <summary>
        /// IIS Metabase key path for the application, e.g. "_LM_W3SVC_1_ROOT_Example". 
        /// </summary>
        /// <remarks>
        /// <note>
        /// <b>Note:</b>  Underscore is used instead of slash.
        /// </note>
        /// </remarks>
        public static string InstanceName
        {
            get
            {
                if (_instanceName == null)
                {
                    _instanceName = GenericHostingEnvironment.ApplicationID;
                    if (_instanceName != null)
                    {
                        _instanceName = _instanceName.Replace("/", "_");
                    }
                }
                return _instanceName == null ? "_Total" : _instanceName;
            }
            set { _instanceName = value; }
        }

#endregion

        /// <summary>
        /// Provides an application-wide implementation of the <see cref="P:System.Web.UI.PartialCachingAttribute.VaryByCustom"/> property.
        /// </summary>
        /// <param name="context">An <see cref="T:System.Web.HttpContext"/> object that contains information about the current Web request.</param>
        /// <param name="custom">The custom string that specifies which cached response is used to respond to the current request.</param>
        /// <returns>
        /// If the value of the <paramref name="custom"/> parameter is "path", the requsets path will be returned.
        /// If the value of the <paramref name="custom"/> parameter is "browser", the browser's <see cref="P:System.Web.Configuration.HttpCapabilitiesBase.Type"/>; otherwise, null.
        /// </returns>
        public override string GetVaryByCustomString(HttpContext context, string custom)
        {
            if (String.Equals(custom, VaryByCustomKey, StringComparison.OrdinalIgnoreCase))
            {
                Url requestUrl = new Url(context.Request.RawUrl);
                return requestUrl.Path;
            }
            return base.GetVaryByCustomString(context, custom);
        }

        /// <summary>
        /// This Init method is called AFTER Application_Start and AFTER the Init methot on HttpModules
        /// Executes custom initialization code after all event handler modules have been added.
        /// </summary>
        /// <remarks>
        /// Note! You cannot count on HttpContext being available - i e do NOT attempt to access it.
        /// </remarks>
        public override void Init()
        {
            _log.Debug("EPiServer.Global.Init Enter");

            // Occurs when an unhandled exception is thrown.
            Error += new EventHandler(Global_Error);

            // Occurs when a security module has established the identity of the user.
            AuthenticateRequest += new EventHandler(Global_AuthenticateRequest);

            // If we will replace the current principal with a VirtualRolePrincipal
            if (VirtualRoles.IsReplacePrincipal)
            {
                PostAuthenticateRequest += new EventHandler(VirtualRolePrincipal.VirtualRolePrincipal_PostAuthenticateRequest);

                // ASP.NET 3.5 handles the role caching correctly, but cannot understand the VirtualRolePrincipal, therefore
                // we have to use our implementation when we wrap the principal with our VirtualRolePrincipal.
                if (System.Web.Security.Roles.Enabled && System.Web.Security.Roles.CacheRolesInCookie)
                {
                    EndRequest += new EventHandler(VirtualRolePrincipal.VirtualRolePrincipal_EndRequest);
                }
            }

            // Occurs just before ASP.NET starts executing an event handler 
            PreRequestHandlerExecute += new EventHandler(Global_PreRequestHandlerExecute);

            // Occurs when when a text should be translated
            EPiServer.XForms.XForm.TranslateEvent += new EPiServer.XForms.XForm.TranslateTextEventHandler(XForm_TranslateEvent);

            // Occurs when a Page Reference should be translated from id to Guid or from Guid to id.
            EPiServer.XForms.MapPageHandler.MapPageEvent += new EPiServer.XForms.MapPageHandler.MapPageEventHandler(XFormData_MapPageEvent);

            _log.Debug("EPiServer.Global.Init Leave");
        }

        /// <summary>
        /// Code to emulate IIS handling of folders / default documents.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        /// <remarks>
        /// Verify if this code is really needed when running under IIS7 - it could most likely be disabled then...
        /// </remarks>
        private void Global_AuthenticateRequest(object sender, EventArgs e)
        {
            // Emulate IIS directory redirection and default document handling.
            string path = Request.Url.AbsolutePath;

            // If we're referring to a directory but no document, try to find an existing default document.
            if (path.EndsWith("/"))
            {
                // Get an array of possible choices for default documents.
                string[] defaultDocuments = GetDefaultDocuments(Request.Url);
                if (defaultDocuments != null)
                {
                    foreach (string defaultDocument in defaultDocuments)
                    {
                        UrlBuilder defaultUrl = new UrlBuilder(Request.Url);
                        defaultUrl.Path += defaultDocument;
                        // If this path actually exists...
                        if (GenericHostingEnvironment.VirtualPathProvider.FileExists(defaultUrl.Path))
                        {
                            // ....rewrite the path to refer to this default document, and serve it up
                            HttpContext.Current.RewritePath(defaultUrl.Path + defaultUrl.Query);
                            break;
                        }
                    }
                }
                return;
            }

            // Request URL does not end with a slash, check if we have a corresponding directory
            if (GenericHostingEnvironment.VirtualPathProvider.DirectoryExists(path))
            {
                // TODO - check if this may be a problem - redirecting with host name?
                // if we're referring to an existing directory, add a "/" and redirect
                path += "/";

                // NOTE! Since we are changing the nesting level of the request, we must do a redirect to ensure
                // that the browser requesting the information is in sync with regards to the base URL.
                Response.Redirect(path);
            }
        }

        /// <summary>
        /// EPiServer support for restricting access to the UI folder and for precompiling .aspx files.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        /// <remarks>
        /// The "precompile" feature is really obsolete as of ASP.NET 2.0, but is retained for backwards compatibility.
        /// </remarks>
        protected virtual void Global_PreRequestHandlerExecute(Object sender, EventArgs e)
        {
            if (Request == null)
            {
                return;
            }

            // Build the arguments for, and raise the ValidateRequestAccess event. This gives the chance to stop
            // processing a request if it attempts to access the UI through an unwanted channel, for example
            // originating from the wrong IP-subnet, through the wrong host etc.
            // This is just a convenience, it can be done by simply attaching to almost any of the HttpHandler requests.
            ValidateRequestAccessEventArgs validateRequestEventArgs = new ValidateRequestAccessEventArgs(Request);
            OnValidateRequestAccess(validateRequestEventArgs);
            if (validateRequestEventArgs.Cancel)
            {
                Response.Clear();
                ExceptionManager.RenderHttpRuntimeError(new HttpException(404, "Not Found"));
                Response.End();
            }

            if (Request.QueryString["EP_PreCompile"] == null)
            {
                return;
            }

            _log.Info("1.2.7 Precompilation request for file " + Request.RawUrl);
            Response.End();
        }

        /// <summary>
        /// Handles the TranslateEvent event of the XForm control.
        /// Occurs when when a text should be translated
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="EPiServer.XForms.TranslateEventArgs"/> instance containing the event data.</param>
        private void XForm_TranslateEvent(object sender, EPiServer.XForms.TranslateEventArgs e)
        {
            e.TranslatedText = LanguageManager.Instance.Translate(e.TextToTranslate);
        }

        /// <summary>
        /// Handles the MapPageEvent event of the XFormData control.
        /// Occurs when a Page Reference should be translated from id to Guid or from Guid to id.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="EPiServer.XForms.MapPageEventArgs"/> instance containing the event data.</param>
        private void XFormData_MapPageEvent(object sender, EPiServer.XForms.MapPageEventArgs e)
        {
            if (e.PageId == 0)
            {
                // Map from Guid to page id
                PermanentPageLinkMap pplm = PermanentPageLinkMap.FindInternal(e.PageGuid);
                e.PageId = pplm == null ? 0 : pplm.PageReference.ID;
            }
            else
            {
                // Map from Guid to page id
                PermanentPageLinkMap pplm = PermanentPageLinkMap.FindInternal(new PageReference(e.PageId));
                e.PageGuid = pplm == null ? Guid.Empty : pplm.Guid;
            }
        }

        /// <summary>
        /// Handles the Error event of the Global control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="evt">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void Global_Error(object sender, EventArgs evt)
        {
            if (HttpContext.Current == null)
            {
                return;
            }

            Exception e;
            try
            {
                //Get the previous exception
                e = Server.GetLastError();
            }
            catch
            {
                return;
            }
            _log.Error("1.2.5 Unhandled exception in ASP.NET", e);

            // Try to get the InconsistentDataException instance that caused the current exception
            InconsistentDataException inconsistent = e.InnerException as InconsistentDataException;
            if (inconsistent != null)
            {
                // The rare case where a delayed long string load fails to retrieve the correct data. Clear cache for
                // the relevant page to avoid that this error happens again for the same page.
                CacheManager.Remove("EPPageData:" + new PageReference(inconsistent.PageLink.ID, EPiServer.Configuration.Settings.Instance.Parent.SiteId).ToString());
            }

            if (!EnableGlobalErrorHandling)
            {
                return;
            }

            if (HttpContext.Current.Items.Contains("EPHandledUnhandledException"))
            {
                return;
            }

            try
            {
                // Clears the previous exception
                Server.ClearError();
                HttpContext.Current.Items.Add("EPHandledUnhandledException", true);
                //Clears all content output from the buffer stream
                Response.Clear();
                //Renders an error message to the user based on the exception
                ExceptionManager.RenderHttpRuntimeError(e);
                Response.End();
            }
            catch 
            {
            }
        }

        /// <summary>
        /// Gets a value indicating whether show error message.
        /// </summary>
        /// <value>
        ///     <c>true</c> If the GlobalErrorHandling flag is "on" or if it sets to "remoteonly" and the request is not locally; otherwise is <c>false</c>.
        /// </value>
        private bool EnableGlobalErrorHandling
        {
            get
            {
                // Read error handling option ["RemoteOnly", "On","Off"]
                string errHandling = Settings.Instance.GlobalErrorHandling;
                // Empty string consider as off
                if (String.IsNullOrEmpty(errHandling))
                {
                    errHandling = "off";
                }
                else
                {
                    errHandling = errHandling.ToLowerInvariant();
                }

                // True if the error handling is on or (if it sets remoteonly and the request is not from local client)
                return (errHandling == "on" || (errHandling == "remoteonly" && !IsRequestLocal));
            }
        }

        /// <summary>
        /// Gets a value indicating whether the IP host address of the client is local.
        /// </summary>
        /// <value>
        ///     <c>true</c> if IP host address of the remote client is local; otherwise, <c>false</c>.
        /// </value>
        private bool IsRequestLocal
        {
            get
            {
                try
                {
                    if (_localHost == null)
                    {
                        _localHost = Dns.GetHostEntry(Dns.GetHostName());
                    }

                    //Converts an IP address string to an System.Net.IPAddress.
                    System.Net.IPAddress remoteAddress = System.Net.IPAddress.Parse(HttpContext.Current.Request.UserHostAddress);
                    return remoteAddress.Equals(System.Net.IPAddress.Loopback) || remoteAddress.Equals(System.Net.IPAddress.IPv6Loopback) || Array.IndexOf(_localHost.AddressList, remoteAddress) >= 0;
                }
                catch 
                {
                    // If there are some errors as result of the IsLocal calculation, treat it as if the request is not local, just to be on the safe side...
                }
                return false;
            }
        }
    }
}

Examples

The following code example demonstrates the usage of Global to read the BaseDiretory.
CopyC#
BaseDirectory:
<%= EPiServer.Global.BaseDirectory %>

Inheritance Hierarchy

See Also