//------------------------------------------------------------------------------
// <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 
...{
public delegate bool 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>
public sealed class CSContext 
...{

Private ContainersPrivate Containers#region Private Containers
//Generally expect 10 or less items
private HybridDictionary _items = new HybridDictionary();
private NameValueCollection _queryString = null;
private string _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>
private void 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

CreateCreate#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>
public static 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>
public static 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>
public static 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>
public static 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>
public static 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>
public static CSContext Create(Uri uri, string appName)
...{
CSContext csContext = new CSContext(uri,appName);
SaveContextToStore(csContext);
return csContext;
}
#endregion

Core PropertiesCore 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>
public object this[string key]
...{
get
...{
return this.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>
public bool IsWebRequest
...{
get...{ return this.Context != null;}
}
public bool IsAuthenticated
...{
get ...{ return !User.IsAnonymous;}
}
public string AuthenticationType
...{
get ...{ return authenticationType; }
set ...{ authenticationType = value.ToLower(); }
}
public string RewrittenUrlName
...{
get...{return _rewrittenUrlName;}
set...{_rewrittenUrlName = value;}
}
public HttpContext Context 
...{
get 
...{
return _httpContext;
}
}
public string 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

HelpersHelpers#region Helpers
public bool IsEmpty
...{
get ...{ return _isEmpty;}
}
public bool IsUserTokenRequest
...{
get ...{return !Globals.IsNullorEmpty(this.Token); }
}
public double AddTimeEvent(object obj)
...{
return this.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>
// ***********************************************************************/
public int 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;
}
public string MapPath(string path)
...{
if(_httpContext != null)
return _httpContext.Server.MapPath(path);
else
// Returns SystemWOW for non web // return Directory.GetCurrentDirectory() + path.Replace("/", @"").Replace("~", "");
return PhysicalPath(path.Replace("/", Path.DirectorySeparatorChar.ToString()).Replace("~", ""));
}
public string PhysicalPath(string path)
...{
return string.Concat(RootPath().TrimEnd(Path.DirectorySeparatorChar), Path.DirectorySeparatorChar.ToString() , path.TrimStart(Path.DirectorySeparatorChar));
}
private string _rootPath = null;
private string 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;
}
private string 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 DataCS 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;
private int _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;
}
return null;
}

/**//**//**//// <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>
public int SettingsID
...{
get
...{
if(_settingsID == -1 && !IsEmpty)
_settingsID = this.SiteSettings.SettingsID;
return _settingsID;
}
}
#endregion

Status PropertiesStatus Properties#region Status Properties
public DateTime RequestStartTime ...{ get ...{ return requestStartTime; } }
public string RolesCacheKey ...{ get ...{ return rolesCacheKey; } set ...{ rolesCacheKey = value; } }
public bool IsUrlReWritten ...{ get ...{ return _isUrlReWritten; } set ...{ _isUrlReWritten = value; } }
public string 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;}
}
private string _hostPath = null;
public string 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;
}
}
private bool _isModal = false;
public bool IsModal
...{
get
...{
return _isModal;
}
set
...{
_isModal = value;
}
}
#endregion

Common QueryString PropertiesCommon QueryString Properties#region Common QueryString Properties

Private MembersPrivate 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
public int 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;}
// }
public int 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;}
}


public int GroupID 
...{
get
...{
if(groupID == -2)
groupID = this.GetIntFromQueryString("GroupID", GetIntFromQueryString("ForumGroupID", -1));
return groupID;
} 
set ...{groupID = value;}
}

public int CategoryID 
...{
get
...{
if(categoryID == -2)
categoryID = this.GetIntFromQueryString("CategoryID", -1);
return categoryID;
} 
set ...{categoryID = value;}
}
public int BlogGroupID 
...{
get
...{
if(blogGroupID == -2)
blogGroupID = this.GetIntFromQueryString("BlogGroupID", -1);
return blogGroupID;
} 
set ...{blogGroupID = value;}
}

public int PostID 
...{
get
...{
if(postID == -2)
postID = this.GetIntFromQueryString("PostID", -1);
return postID;
} 
set ...{postID = value;}
}
public int ThreadID 
...{
get
...{
if(threadID == -2)
threadID = this.GetIntFromQueryString("ThreadID", -1);
return threadID;
} 
set ...{threadID = value;}
}
public int UserID 
...{
get
...{
if(userID == -2)
userID = this.GetIntFromQueryString("UserID", -1);
return userID;
} 
set ...{userID = value;}
}
public string UserName 
...{
get
...{
if(userName == null)
...{
userName = QueryString["UserName"];
}
return userName;
}
set
...{
userName = value;
}
}
public string Token
...{
get ...{ return QueryString["Token"];}
}
public Guid RoleID 
...{
get
...{
if(roleID == Guid.Empty)
roleID = GetGuidFromQueryString("RoleID");
return roleID;
} 
set ...{roleID = value;}
}


public string QueryText
...{
get
...{
if(queryText == null)
queryText = QueryString["q"];
return queryText;
}
set ...{queryText = value;}
}
public string ReturnUrl
...{
get
...{
if(returnUrl == null)
returnUrl = QueryString["returnUrl"];
return returnUrl;
}
set ...{returnUrl = value;}
}
public string Url
...{
get
...{
if(url == null)
url = QueryString["url"];
return url;
}
set ...{url = value;}
}
public string Args
...{
get
...{
if(args == null)
args = QueryString["args"];
return args;
}
set ...{args = value;}
}
public int 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;
else if(pageIndex < 0)
pageIndex = 0;
}
}
return pageIndex;
} 
set ...{pageIndex = value;}
}

public string ApplicationKey
...{
get
...{
if(appKey == null)
...{
appKey = ApplicationKeyProvider.Instance().GetKey();
}
return appKey;
}
set ...{appKey = value;}
}

#endregion

StateState#region State
private static readonly string 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>
public static 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)
throw new 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;
}
}
private static LocalDataStoreSlot GetSlot()
...{
return Thread.GetNamedDataSlot(dataKey);
}
private static void SaveContextToStore(CSContext context)
...{
if(context.IsWebRequest)
...{
context.Context.Items[dataKey] = context;
}
else
...{
Thread.SetData(GetSlot(), context);
}
}
public static void 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请求时用的,比如单元测试等等。
CSContext在CS中的作用很重要,理解它是理解CS工作原理的前提,说到低它就是为了共享数据而出现的,在用户控件组成页面的CS中共享数据显得尤为重要,这样的设计方法借鉴到自己的项目也是个很好的选择。
发表于 @ 2006年11月09日 14:00:00 | 评论( loading... ) | 举报| 收藏