CSContext //------------------------------------------------------------------------------ // <copyright company="Telligent Systems"> // Copyright (c) Telligent Systems Corporation. All rights reserved. // </copyright> //------------------------------------------------------------------------------ using System; using System.Collections.Specialized; using System.IO; using System.Threading; using System.Web; using System.Collections; using CommunityServer.Configuration; namespace CommunityServer.Components { publicdelegatebool UrlReWriterDelegate(HttpContext context); /**////<summary> /// The CSContext represents common properties and settings used through out of a Request. All data stored /// in the context will be cleared at the end of the request /// /// This object should be safe to use outside of a web request, but querystring and other values should be prepopulated /// /// Each CS thread must create an instance of the CSContext using one of the Three Create overloads. In some cases, /// the CreateEmptyContext method may be used, but it is NOT recommended. ///</summary> publicsealedclass CSContext { Private Containers#region Private Containers //Generally expect 10 or less items private HybridDictionary _items =new HybridDictionary(); private NameValueCollection _queryString =null; privatestring _siteUrl =null, _rewrittenUrlName =null; private Uri _currentUri; string rolesCacheKey =null; string authenticationType ="forms"; bool _isUrlReWritten =false; bool _isEmpty =false; string _rawUrl; HttpContext _httpContext =null; DateTime requestStartTime = DateTime.Now; private PageTimeStatus pageTimeStatus =new PageTimeStatus(); #endregion Initialize and cnstr.'s#region Initialize and cnstr.'s /**////<summary> /// Create/Instatiate items that will vary based on where this object /// is first created /// /// We could wire up Path, encoding, etc as necessary ///</summary> privatevoid Initialize(NameValueCollection qs, Uri uri, string rawUrl, string siteUrl) { _queryString = qs; _siteUrl = siteUrl; _currentUri = uri; _rawUrl = rawUrl; } /**////<summary> /// cntr called when no HttpContext is available ///</summary> private CSContext(Uri uri, string siteUrl) { Initialize(new NameValueCollection(), uri, uri.ToString(), siteUrl); } /**////<summary> /// cnst called when HttpContext is avaiable ///</summary> ///<param name="context"></param> private CSContext(HttpContext context, bool includeQS) { this._httpContext = context; if(includeQS) { Initialize(new NameValueCollection(context.Request.QueryString), context.Request.Url, context.Request.RawUrl, GetSiteUrl()); } else { Initialize(null, context.Request.Url, context.Request.RawUrl, GetSiteUrl()); } } #endregion Create#region Create /**////<summary> /// Creates a empty context to be used for thread data storage. This instance of context will not be able to look up a SiteSettings or User. /// This method is NOT recommended since most of the API relies on a valid User and SiteSettings objects. ///</summary> ///<returns></returns> publicstatic CSContext CreateEmptyContext() { CSContext csContext =new CSContext(new Uri("http://CreateEmptyContext"),"http://CreateEmptyContext"); csContext._isEmpty =true; SaveContextToStore(csContext); return csContext; } /**////<summary> /// Creates a new context and sets the SiteSettings to the specific SettingsID. It's primary usage will be for background threads/tasks/offline. ///</summary> ///<param name="settingsID">Settings to look up and bind to.</param> ///<returns></returns> publicstatic CSContext Create(int settingsID) { CSContext csContext =new CSContext(new Uri("http://CreateContextBySettingsID"),"http://CreateContextBySettingsID"); csContext.SiteSettings = SiteSettingsManager.GetSiteSettings(settingsID); SaveContextToStore(csContext); return csContext; } /**////<summary> /// Creates a Context instance based on HttpContext. Generally, this /// method should be called via Begin_Request in an HttpModule ///</summary> publicstatic CSContext Create(HttpContext context) { return Create(context,false); } /**////<summary> /// Creates a Context instance based on HttpContext. Generally, this /// method should be called via Begin_Request in an HttpModule ///</summary> publicstatic CSContext Create(HttpContext context, bool isReWritten) { CSContext csContext =new CSContext(context,true); csContext.IsUrlReWritten = isReWritten; SaveContextToStore(csContext); return csContext; } /**////<summary> /// On occasion, it may be necessary to use the CSContext during UrlRewriting. By default, it is generally not /// in the correct state and should not be accessed. The signature below will enable the CSContext to be created, /// saved (so it is available globally) and then perform the UrlRewritng via a delegate. /// /// Except for QueryString values, the full CSContext should be availble during UrlRewriting. ///</summary> ///<param name="context"></param> ///<param name="rewriter"></param> ///<returns></returns> publicstatic CSContext Create(HttpContext context, UrlReWriterDelegate rewriter) { CSContext csContext =new CSContext(context,false); SaveContextToStore(csContext); csContext.IsUrlReWritten = rewriter(context); csContext._queryString =new NameValueCollection(context.Request.QueryString); return csContext; } /**////<summary> /// Creates a Context instance based. If the HttpContext is available it will be used. /// Generally this method should be used when working with CS outside of a valid Web Request ///</summary> publicstatic CSContext Create(Uri uri, string appName) { CSContext csContext =new CSContext(uri,appName); SaveContextToStore(csContext); return csContext; } #endregion Core Properties#region Core Properties /**////<summary> /// Simulates Context.Items and provides a per request/instance storage bag ///</summary> public IDictionary Items { get{ return _items;} } public PageTimeStatus PageTimeStatus { get{ return pageTimeStatus; } set{ pageTimeStatus = value; } } /**////<summary> /// Provides direct access to the .Items property ///</summary> publicobjectthis[string key] { get { returnthis.Items[key]; } set { this.Items[key] = value; } } /**////<summary> /// Allows access to QueryString values ///</summary> public NameValueCollection QueryString { get{return _queryString;} } /**////<summary> /// Quick check to see if we have a valid web reqeust. Returns false if HttpContext == null ///</summary> publicbool IsWebRequest { get{ returnthis.Context !=null;} } publicbool IsAuthenticated { get{ return!User.IsAnonymous;} } publicstring AuthenticationType { get{ return authenticationType; } set{ authenticationType = value.ToLower(); } } publicstring RewrittenUrlName { get{return _rewrittenUrlName;} set{_rewrittenUrlName = value;} } public HttpContext Context { get { return _httpContext; } } publicstring SiteUrl { get{ return _siteUrl; } } private Guid _anonUserId = Guid.Empty; public Guid AnonymousUserID { get { if(_anonUserId == Guid.Empty &&this.IsWebRequest) _anonUserId = Users.GetAnonyousUserTrackingID(this.Context); return _anonUserId; } } #endregion Helpers#region Helpers publicbool IsEmpty { get{ return _isEmpty;} } publicbool IsUserTokenRequest { get{return!Globals.IsNullorEmpty(this.Token); } } publicdouble AddTimeEvent(object obj) { returnthis.PageTimeStatus.AddTimeEvent(obj.GetType()); } // ********************************************************************* // GetGuidFromQueryString // /**////<summary> /// Retrieves a value from the query string and returns it as an int. ///</summary> // ***********************************************************************/ public Guid GetGuidFromQueryString(string key) { Guid returnValue = Guid.Empty; string queryStringValue; // Attempt to get the value from the query string // queryStringValue = QueryString[key]; // If we didn't find anything, just return // if (queryStringValue ==null) return returnValue; // Found a value, attempt to conver to integer // try { // Special case if we find a # in the value // if (queryStringValue.IndexOf("#") >0) queryStringValue = queryStringValue.Substring(0, queryStringValue.IndexOf("#")); returnValue =new Guid(queryStringValue); } catch{} return returnValue; } // ********************************************************************* // GetIntFromQueryString // /**////<summary> /// Retrieves a value from the query string and returns it as an int. ///</summary> // ***********************************************************************/ publicint GetIntFromQueryString(string key, int defaultValue) { string queryStringValue; // Attempt to get the value from the query string // queryStringValue =this.QueryString[key]; // If we didn't find anything, just return // if (queryStringValue ==null) return defaultValue; // Found a value, attempt to conver to integer // try { // Special case if we find a # in the value // if (queryStringValue.IndexOf("#") >0) queryStringValue = queryStringValue.Substring(0, queryStringValue.IndexOf("#")); defaultValue = Convert.ToInt32(queryStringValue); } catch{} return defaultValue; } publicstring MapPath(string path) { if(_httpContext !=null) return _httpContext.Server.MapPath(path); else // Returns System/WOW for non web // return Directory.GetCurrentDirectory() + path.Replace("/", @"/").Replace("~", ""); return PhysicalPath(path.Replace("/", Path.DirectorySeparatorChar.ToString()).Replace("~", "")); } publicstring PhysicalPath(string path) { returnstring.Concat(RootPath().TrimEnd(Path.DirectorySeparatorChar), Path.DirectorySeparatorChar.ToString() , path.TrimStart(Path.DirectorySeparatorChar)); } privatestring _rootPath =null; privatestring RootPath() { if(_rootPath ==null) { _rootPath = AppDomain.CurrentDomain.BaseDirectory; string dirSep = Path.DirectorySeparatorChar.ToString(); _rootPath = _rootPath.Replace("/", dirSep); string filePath = Config.FilesPath; if(filePath !=null) { filePath = filePath.Replace("/", dirSep); if(filePath.Length >0&& filePath.StartsWith(dirSep) && _rootPath.EndsWith(dirSep)) { _rootPath = _rootPath + filePath.Substring(1); } else { _rootPath = _rootPath + filePath; } } } return _rootPath; } privatestring GetSiteUrl() { string appOverride =this.Config.ApplicationOverride; if(appOverride !=null) return appOverride; //NOTE: Watch this change. Should be safe, but not tested. //Virtualization means urls must be very precise. string hostName = _httpContext.Request.Url.Host.Replace("www.",string.Empty); string applicationPath = _httpContext.Request.ApplicationPath; if(applicationPath.EndsWith("/")) applicationPath = applicationPath.Remove(applicationPath.Length-1,1); return hostName + applicationPath; } #endregion CS Data#region CS Data private User _currentUser =null; private SiteSettings _currentSettings =null; private CSConfiguration _config =null; private SiteStatistics _stats =null; private Section _section =null; private Group _group =null; private Post _post =null; privateint _settingsID =-1; /**////<summary> /// Return the current logged in User. This user value to be anonymous if no user is logged in. /// /// If this context is IsEmpty (ie, SiteSettings == null) then null will be returned by default. /// /// This value can be set if necessary ///</summary> public User User { get { //We can not look up a user without SiteSettings if(_currentUser ==null&& SiteSettings !=null) { _currentUser = TokenUser(); if(_currentUser ==null) _currentUser= Users.GetUser(true); } return _currentUser; } set{ _currentUser = value;} } private User TokenUser() { if(!Globals.IsNullorEmpty(this.Token) &&!Globals.IsNullorEmpty(this.UserName)) { Guid g =new Guid(this.Token); User user = Users.GetUser(0,this.UserName,true,true); if(user !=null&&!user.IsAnonymous && user.PublicToken == g) return user; } returnnull; } /**////<summary> /// Settings for the current site. This by default, it will use the value found in this.SiteUrl. /// /// This value is set by ID when using .Create(int settingsID). If IsEmpty == false, this method will /// return false unless the SiteSettings is explicitly set. ///</summary> public SiteSettings SiteSettings { get { //If this context is empty, we can not look up a SiteSetting. //We can still set the setting if necessary if(_currentSettings ==null&&!IsEmpty) _currentSettings = SiteSettingsManager.GetSiteSettings(this.SiteUrl); return _currentSettings; } set{ _currentSettings = value;} } /**////<summary> /// Returnt the current configuration found in the communityserver.config file ///</summary> public CSConfiguration Config { get { if(_config ==null) _config = CSConfiguration.GetConfig(); return _config; } } /**////<summary> /// Return the site statistics for the current SiteSettings. If SiteSettings is null (usually because of using the Emtpy setting) /// this propety will return null. In other words, there needs to be a valid SiteSettings first. ///</summary> public SiteStatistics Statistics { get { //Same as user. No SiteSettings, no Statistics if(_stats ==null&& SiteSettings !=null) _stats = SiteStatistics.LoadSiteStatistics(this.SiteSettings.SettingsID,true,3); return _stats; } } /**////<summary> /// Container for the current post. This must be explicitly or it will always bee null ///</summary> public Post Post { get{ return _post;} set{_post = value;} } /**////<summary> /// Container for the current section. This must be explicitly or it will always bee null ///</summary> public Section Section { get{ return _section;} set{_section = value;} } /**////<summary> /// Container for the current group. This must be explicitly or it will always bee null ///</summary> public Group Group { get{ return _group;} set{_group = value;} } /**////<summary> /// Shortcut to SiteSettings.SettingsID. This proprty will return -1 if /// the SiteSettings can not be found (or IsEmtpy == true) ///</summary> publicint SettingsID { get { if(_settingsID ==-1&&!IsEmpty) _settingsID =this.SiteSettings.SettingsID; return _settingsID; } } #endregion Status Properties#region Status Properties public DateTime RequestStartTime { get{ return requestStartTime; } } publicstring RolesCacheKey { get{ return rolesCacheKey; }set{ rolesCacheKey = value; } } publicbool IsUrlReWritten { get{ return _isUrlReWritten; }set{ _isUrlReWritten = value; } } publicstring RawUrl { get{ return _rawUrl; }set{ _rawUrl = value; } } public ApplicationType ApplicationType { get{return Config.AppLocation.CurrentApplicationType;}} public Uri CurrentUri { get { if(_currentUri ==null) _currentUri =new Uri("http://localhost/cs"); return _currentUri; }set{_currentUri = value;} } privatestring _hostPath =null; publicstring HostPath { get { if(_hostPath ==null) { string portInfo = CurrentUri.Port ==80?string.Empty : ":"+ CurrentUri.Port.ToString(); _hostPath =string.Format("{0}://{1}{2}",CurrentUri.Scheme,CurrentUri.Host, portInfo); } return _hostPath; } } privatebool _isModal =false; publicbool IsModal { get { return _isModal; } set { _isModal = value; } } #endregion Common QueryString Properties#region Common QueryString Properties Private Members#region Private Members int sectionID =-2; int categoryID =-2; int messageID =-2; int groupID =-2; int postID =-2; int threadID =-2; int userID =-2; string userName =null; int pageIndex =-2; int blogGroupID =-2; Guid roleID = Guid.Empty; string queryText =null; string returnUrl =null; string appKey =null; string url =null; string args =null; #endregion publicint MessageID { get { if(messageID ==-2) messageID =this.GetIntFromQueryString("MessageID", -1); return messageID; } set{messageID = value;} } // [Obsolete("ForumID is obsolete, use the SectionID property")] // public int ForumID // { // get // { // if(sectionID == -2) // sectionID = this.GetIntFromQueryString("ForumID", -1); // // return sectionID; // } // set {sectionID = value;} // } publicint SectionID { get { if(sectionID ==-2) { //Phasing out calls to ForumID. For now, if SectioID fails to be found, we default to ForumID sectionID = GetIntFromQueryString("SectionID", GetIntFromQueryString("ForumID",-1)); } return sectionID; } set{sectionID = value;} } publicint GroupID { get { if(groupID ==-2) groupID =this.GetIntFromQueryString("GroupID", GetIntFromQueryString("ForumGroupID", -1)); return groupID; } set{groupID = value;} } publicint CategoryID { get { if(categoryID ==-2) categoryID =this.GetIntFromQueryString("CategoryID", -1); return categoryID; } set{categoryID = value;} } publicint BlogGroupID { get { if(blogGroupID ==-2) blogGroupID =this.GetIntFromQueryString("BlogGroupID", -1); return blogGroupID; } set{blogGroupID = value;} } publicint PostID { get { if(postID ==-2) postID =this.GetIntFromQueryString("PostID", -1); return postID; } set{postID = value;} } publicint ThreadID { get { if(threadID ==-2) threadID =this.GetIntFromQueryString("ThreadID", -1); return threadID; } set{threadID = value;} } publicint UserID { get { if(userID ==-2) userID =this.GetIntFromQueryString("UserID", -1); return userID; } set{userID = value;} } publicstring UserName { get { if(userName ==null) { userName = QueryString["UserName"]; } return userName; } set { userName = value; } } publicstring Token { get{ return QueryString["Token"];} } public Guid RoleID { get { if(roleID == Guid.Empty) roleID = GetGuidFromQueryString("RoleID"); return roleID; } set{roleID = value;} } publicstring QueryText { get { if(queryText ==null) queryText = QueryString["q"]; return queryText; } set{queryText = value;} } publicstring ReturnUrl { get { if(returnUrl ==null) returnUrl = QueryString["returnUrl"]; return returnUrl; } set{returnUrl = value;} } publicstring Url { get { if(url ==null) url = QueryString["url"]; return url; } set{url = value;} } publicstring Args { get { if(args ==null) args = QueryString["args"]; return args; } set{args = value;} } publicint PageIndex { get { if(pageIndex ==-2) { // load page number from AJAX parameter first if (this.Context !=null&& AjaxManager.IsCallBack && AjaxManager.CallBackMethod =="GetPage"&&!Globals.IsNullorEmpty(this.Context.Request.Form["Ajax_CallBackArgument0"])) pageIndex =int.Parse(this.Context.Request.Form["Ajax_CallBackArgument0"]) -1; else { pageIndex =this.GetIntFromQueryString("PageIndex", GetIntFromQueryString("p",-1)); if(pageIndex !=-1) pageIndex = pageIndex -1; elseif(pageIndex <0) pageIndex =0; } } return pageIndex; } set{pageIndex = value;} } publicstring ApplicationKey { get { if(appKey ==null) { appKey = ApplicationKeyProvider.Instance().GetKey(); } return appKey; } set{appKey = value;} } #endregion State#region State privatestaticreadonlystring dataKey ="CSContextStore"; /**////<summary> /// Returns the current instance of the CSContext from the ThreadData Slot. If one is not found and a valid HttpContext can be found, /// it will be used. Otherwise, an exception will be thrown. ///</summary> publicstatic CSContext Current { get { HttpContext httpContext = HttpContext.Current; CSContext context =null; if(httpContext !=null) { context = httpContext.Items[dataKey] as CSContext; } else { context = Thread.GetData(GetSlot()) as CSContext; } if (context ==null) { if(httpContext ==null) thrownew Exception("No CSContext exists in the Current Application. AutoCreate fails since HttpContext.Current is not accessible."); context =new CSContext(httpContext,true); SaveContextToStore(context); } return context; } } privatestatic LocalDataStoreSlot GetSlot() { return Thread.GetNamedDataSlot(dataKey); } privatestaticvoid SaveContextToStore(CSContext context) { if(context.IsWebRequest) { context.Context.Items[dataKey] = context; } else { Thread.SetData(GetSlot(), context); } } publicstaticvoid Unload() { Thread.FreeNamedDataSlot(dataKey); } #endregion } }
Private Containers :一些简单的内部字段; Initialize and cnstr.'s:初始化和私有的构造函数; Create:静态的Create方法,通过这些冲载的Create方法,在最先调用的时候就构造了CSContext的一个对象; Core Properties:一些核心的属性,这些属性提供了CSContext最常用的功能。其中Items这个的作用是在当前请求过程中,如果有那些常用的针对请求者的数据保存在此,避免在一次请求多次处理,保存在此后,当前请求的其他地方需要用到此数据时就不需要再处理了。我们可以看到在解决方案里搜索“csContext.Items”这样的关键字找到这些应用。 Helpers:为此类的其他方法提供处理程序的一组方法。 CS Data:保存CS特有的公用数据,比如User、SiteSettings、Post、Section等等,这些都是可以公用的数据,所以统一放在这里,一般在用户控件的基类会有这些数据的处理,所以在我们使用的时候调用这些公用数据很方便。 Status Properties:请求状态的一组属性。 Common QueryString Properties:通过请求参数获取一些公用的数据,这些都是在CS中非常常用的参数,如:sectionID、GroupID、postID等等,在应用过程中我们子需要直接使用这些属性就可以了。 State:一组静态属性和方法,在第一次请求的时候通过调用Create方法创建CSContext对象并将对象保存到HttpContext,当以后需要获取CSContext对象的时候再从HttpContext获取,同时CSContext也保存有HttpContext对象的引用。在这个组里还有一个很重要的方法,可以把CSContext保存到其他区域(非HttpContext的地方),这主要是为了提供非Http请求时用的,比如单元测试等等。