using System; using System.Data; using System.Configuration; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.HtmlControls; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using BlogEngine.Core; using System.IO; using BlogEngine.Core.Web.Controls; using System.Xml; using System.Web.Caching; using System.Collections; using System.Collections.Generic; /// /// Summary description for Counter /// [Extension("PostViewCounter", "1.0", "chicken")] public class PostViewCounter { private static ExtensionSettings _settings = null; public PostViewCounter() { Post.Serving += new EventHandler(OnPostServing); ExtensionSettings settings = new ExtensionSettings("PostViewCounter"); settings.AddParameter( "MaxHitRecordCount", "最多保留筆數:"); settings.AddParameter( "HitRecordTTL", "最長保留天數:"); settings.AddValues(new string[] { "500", "90" }); //settings.ShowAdd = false; //settings.ShowDelete = false; //settings.ShowEdit = true; settings.IsScalar = true; settings.Help = "設定 counter hit records 保留筆數及時間。只有在筆數限制內且沒有超過保留期限的記錄才會被留下來。"; ExtensionManager.ImportSettings(settings); _settings = ExtensionManager.GetSettings("PostViewCounter"); } public static int MaxHitRecordCount { get { return int.Parse(_settings.GetSingleValue("MaxHitRecordCount")); } } public static int HitRecordTTL { get { return int.Parse(_settings.GetSingleValue("HitRecordTTL")); } } private void OnPostServing(object sender, ServingEventArgs e) { IPublishable ipub = ((IPublishable)sender); string body = String.Empty; if (e.Location == ServingLocation.SinglePost && HttpContext.Current.Request.IsAuthenticated == false) { Counter.Hit(ipub.Id.ToString()); } } public class Counter { private string _counterID = null; private string _dataFilePath = null; private string DataFilePath { get { return this._dataFilePath; } } public Counter(string counterID) { this._counterID = counterID; // // build path // string dataFolder = Path.Combine(HttpContext.Current.Server.MapPath(BlogSettings.Instance.StorageLocation), "counter"); if (Directory.Exists(DataFilePath) == false) Directory.CreateDirectory(dataFolder); this._dataFilePath = Path.Combine(dataFolder, this._counterID + ".xml"); } public void Hit() { // // add count and save to files. 要處理好 lock 的問題 // FileStream fs = File.Open( this.DataFilePath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None); XmlDocument xmldoc = new XmlDocument(); if (fs.Length == 0) { xmldoc.LoadXml(""); } else { xmldoc.Load(fs); fs.Seek(0, SeekOrigin.Begin); fs.SetLength(0); this.Compact(xmldoc); } XmlElement hitelem = xmldoc.CreateElement("hit"); xmldoc.DocumentElement.AppendChild(hitelem); hitelem.SetAttribute("time", DateTime.Now.ToString("s")); hitelem.SetAttribute("referer", HttpContext.Current.Request.ServerVariables["HTTP_REFERER"]); hitelem.SetAttribute("remote-host", HttpContext.Current.Request.ServerVariables["REMOTE_HOST"]); hitelem.SetAttribute("user-agent", HttpContext.Current.Request.ServerVariables["HTTP_USER_AGENT"]); xmldoc.Save(fs); fs.Close(); } public void Compact() { if (File.Exists(this.DataFilePath) == false) return; FileStream fs = File.Open( this.DataFilePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None); XmlDocument xmldoc = new XmlDocument(); xmldoc.Load(fs); fs.Seek(0, SeekOrigin.Begin); fs.SetLength(0); this.Compact(xmldoc); xmldoc.Save(this.DataFilePath); fs.Close(); } private void Compact(XmlDocument xmldoc) { // // check compact settings // int padCount = 0; XmlNodeList hits = xmldoc.DocumentElement.SelectNodes("hit"); foreach (XmlElement hit in hits) { if (DateTime.Parse(hit.GetAttribute("time")).AddDays(HitRecordTTL) > DateTime.Now && (hits.Count - padCount) < MaxHitRecordCount) { break; } // // timeout or exceed max count // hit.ParentNode.RemoveChild(hit); padCount++; } if (padCount > 0) { int baseCount = 0; if (xmldoc.DocumentElement.HasAttribute("base") == true) { baseCount = int.Parse(xmldoc.DocumentElement.GetAttribute("base")); } xmldoc.DocumentElement.SetAttribute("base", (baseCount + padCount).ToString()); } } public int GetTotalCount() { if (File.Exists(this.DataFilePath) == false) { return 0; } // // 從檔案載入總筆數。要透過 CACHE 輔助 // string cacheKey = "counter-cache-" + this._counterID; int? value = HttpRuntime.Cache[cacheKey] as int?; if (value == null) { XmlDocument xmldoc = new XmlDocument(); xmldoc.Load(this.DataFilePath); int baseCount = (xmldoc.DocumentElement.HasAttribute("base")) ? (int.Parse(xmldoc.DocumentElement.GetAttribute("base"))) : (0); int hitCount = xmldoc.DocumentElement.SelectNodes("hit").Count; value = baseCount + hitCount; HttpRuntime.Cache.Insert( cacheKey, value, new CacheDependency(this.DataFilePath)); } return value.Value; } public static int GetTotalCount(string counterID) { return (new Counter(counterID)).GetTotalCount(); } public static void Hit(string counterID) { (new Counter(counterID)).Hit(); } public static void Compact(string counterID) { (new Counter(counterID)).Compact(); } public static int TotalPostsCount() { int total = 0; foreach (Post post in Post.Posts) { total += GetTotalCount(post.Id.ToString()); } return total; } } }