Skip to content

Instantly share code, notes, and snippets.

@johnkchow
Created February 18, 2011 03:02
Show Gist options
  • Save johnkchow/833179 to your computer and use it in GitHub Desktop.
Save johnkchow/833179 to your computer and use it in GitHub Desktop.
MvcScriptController - Compresses all script in a global js directory into one file HtmlHelperExtension - automatically generate <link> elements for css files depending on controller/action
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using System.Web;
using System.IO;
using Core.Service;
namespace NestleGFGL.Extensions.Helpers
{
public static class HtmlHelperExtension
{
private const string _scriptHtmlFormat = "<script type=\"text/javascript\" src=\"{0}\"></script>";
public static IHtmlString GetGlobalScriptsTag(this HtmlHelper html)
{
UrlHelper urlHelper = new UrlHelper(html.ViewContext.RequestContext);
string baseUrl = urlHelper.RouteUrl(new { controller = "MvcScript", action = "GetGlobalScripts" });
string url = string.Format("{0}/{1}/{2}", baseUrl, html.ViewData["controller"], html.ViewData["action"]);
return new HtmlString(string.Format(_scriptHtmlFormat, url));
}
public static IHtmlString GetCssLinks(this HtmlHelper html)
{
CssManager cssManager = new CssManager();
return cssManager.GetCssLinks(html);
}
}
// Helper classes below
internal class CssManager
{
private const string _defaultCssRootDir = "~/content";
private const string _globalDir = "global";
private const string _cssExt = "css";
private const string _cssLinksCacheKey = "cache_key_css_links_{0}_{1}";
private const string _linkFormat = "<link rel=\"stylesheet\" type=\"text/css\" href=\"{0}\">";
public CssManager() : this(new ServerPathMapper())
{
}
public CssManager(IPathMapper mapper)
{
PathMapper = mapper;
}
public IHtmlString GetCssLinks(HtmlHelper html)
{
object returnedValue;
string controllerName = html.ViewData.TryGetValue("controller", out returnedValue) ? returnedValue as string : string.Empty;
string actionName = html.ViewData.TryGetValue("action", out returnedValue) ? returnedValue as string : string.Empty;
string cacheKey = GetCacheKey(controllerName, actionName);
IHtmlString htmlString = CoreManager.Cache.Get<IHtmlString>(cacheKey);
if (htmlString != null)
return htmlString;
StringBuilder linksBuilder = new StringBuilder();
List<FileInfo> cssFiles = new List<FileInfo>();
AddFiles(ref cssFiles, BuildUrl(_defaultCssRootDir, _globalDir), _cssExt);
AddFiles(ref cssFiles, BuildUrl(_defaultCssRootDir, controllerName), _cssExt);
AddFiles(ref cssFiles, BuildUrl(_defaultCssRootDir, controllerName, actionName), _cssExt);
if (cssFiles != null)
{
foreach (FileInfo file in cssFiles)
{
linksBuilder.AppendFormat(_linkFormat, BuildUrl(_defaultCssRootDir, _globalDir, file.Name));
}
}
htmlString = new HtmlString(string.Format(linksBuilder.ToString()));
CoreManager.Cache.Set(cacheKey, htmlString);
return htmlString;
}
protected string GetCacheKey(string controllerName, string actionName)
{
return string.Format(_cssLinksCacheKey, controllerName, actionName);
}
protected string BuildUrl(params object[] urlSections)
{
if (urlSections == null || urlSections.Length <= 0)
{
return string.Empty;
}
StringBuilder sb = new StringBuilder();
foreach (object section in urlSections)
{
sb.AppendFormat("{0}/", section);
}
// remove trailing slash
sb.Remove(sb.Length - 1, 1);
return sb.ToString();
}
protected void AddFiles(ref List<FileInfo> fileList, string relativeDirPath, string extension)
{
if (fileList == null)
fileList = new List<FileInfo>();
List<FileInfo> files = GetFiles(relativeDirPath, extension);
if (files != null)
fileList.AddRange(files);
}
protected List<FileInfo> GetFiles(string relativeDirPath, string extension)
{
string path = PathMapper.MapPath(relativeDirPath);
if (!Directory.Exists(path))
return null;
List<FileInfo> files = new List<FileInfo>();
string[] fileNames = Directory.GetFiles(path, "*." + extension, SearchOption.TopDirectoryOnly);
foreach (string name in fileNames)
{
files.Add(new FileInfo(name));
}
return files;
}
private IPathMapper PathMapper { get; set; }
}
public interface IPathMapper
{
string MapPath(string relativePath);
string MapPath();
}
public class ServerPathMapper : IPathMapper
{
private readonly string _relativePath;
public ServerPathMapper()
{
}
public ServerPathMapper(string relativePath)
{
_relativePath = relativePath;
}
#region Implementation of IPathMapper
public string MapPath(string relativePath)
{
return HttpContext.Current.Server.MapPath(relativePath);
}
public string MapPath()
{
if (_relativePath != null)
if (HttpContext.Current != null) return HttpContext.Current.Server.MapPath(_relativePath);
return null;
}
#endregion
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.IO;
using System.Text;
using System.Configuration;
using System.Web.UI;
using NestleGFGL.Site.Code.Base;
using NestleGFGL.Extensions.Helpers;
namespace NestleGFGL.Site.Controllers
{
public class MvcScriptController : BaseController
{
private const string _jsExt = "js";
private const string _cssExt = "css";
private const string DefaultJsDir1 = "~/js/global";
private const string DefaultJsDir2 = "~/scripts/global";
private const string DefaultCssDir = "~/content";
private const string DefaultGlobalDirectoryName = "global";
private const string GlobalJsDirectoryAppKey = "GlobalJSDirectory";
private const string GlobalCssDirectoryAppKey = "GlobalCssDirectory";
public MvcScriptController()
{
ServerPathMapper = new ServerPathMapper();
}
[OutputCache(Duration = 900, Location = OutputCacheLocation.ServerAndClient)]
public FileResult GetGlobalScripts()
{
StringBuilder sb = LoadGlobalJavascriptScripts();
return StreamText(sb.ToString(), JsExt);
}
[OutputCache(Duration = 900, Location = OutputCacheLocation.ServerAndClient)]
public FileResult GetStyleSheets(string controllerName)
{
StringBuilder sb = LoadStyleSheets(controllerName);
return StreamText(sb.ToString(), CssExt);
}
[OutputCache(Duration = 900, Location = OutputCacheLocation.ServerAndClient)]
public FileResult GetGlobalStyleSheets()
{
StringBuilder sb = LoadStyleSheets(DefaultGlobalDirectoryName);
return StreamText(sb.ToString(), CssExt);
}
private StringBuilder LoadGlobalJavascriptScripts()
{
StringBuilder sb = new StringBuilder();
string jsDirectory;
if (!string.IsNullOrEmpty(ConfigurationManager.AppSettings.Get(GlobalJsDirectoryAppKey)))
{
jsDirectory = ConfigurationManager.AppSettings.Get(GlobalJsDirectoryAppKey);
}
else
{
jsDirectory = GetDefaultGlobalJavascriptDirectory();
}
// Filter any vsdoc files
IEnumerable<FileInfo> jsFiles = GetFiles(jsDirectory, JsExt).Where(f => !f.Name.ToLower().EndsWith("-vsdoc.js"));
// Filter minimized js files in Debug Mode
if (IsDebugMode)
{
jsFiles = jsFiles.Where(f => !f.Name.ToLower().EndsWith("min.js"));
}
if (jsFiles != null)
{
foreach (FileInfo file in jsFiles)
{
using (StreamReader reader = new StreamReader(file.OpenRead()))
{
sb.Append(reader.ReadToEnd());
}
}
}
return sb;
}
/// <summary>
/// Gets stylesheet from path relative to the css root directory
/// </summary>
/// <param name="relativePath"></param>
/// <returns></returns>
private StringBuilder LoadStyleSheets(string relativePath)
{
if(relativePath.StartsWith("/") || relativePath.StartsWith("~"))
throw new ArgumentException("The relative path cannot start with '/' or '~'.");
// get css root dir
string rootDir = !string.IsNullOrEmpty(ConfigurationManager.AppSettings.Get(GlobalCssDirectoryAppKey)) ?
ConfigurationManager.AppSettings.Get(GlobalCssDirectoryAppKey) : GetDefaultRootStyleSheetDirectory();
if (!rootDir.EndsWith("/"))
rootDir = string.Concat(rootDir, "/");
string cssDir = string.Concat(rootDir, relativePath);
List<FileInfo> files = GetFiles(cssDir, CssExt) ?? new List<FileInfo>();
StringBuilder sb = new StringBuilder();
foreach (FileInfo file in files)
{
using (StreamReader reader = new StreamReader(file.OpenRead()))
{
sb.Append(reader.ReadToEnd());
}
}
return sb;
}
private List<FileInfo> GetFiles(string dirPath, string extension)
{
string path = ServerPathMapper.MapPath(dirPath);
if (!Directory.Exists(path))
return null;
List<FileInfo> files = new List<FileInfo>();
string[] fileNames = Directory.GetFiles(path, "*." + extension, SearchOption.TopDirectoryOnly);
foreach (string name in fileNames)
{
files.Add(new FileInfo(name));
}
return files;
}
private string GetDefaultGlobalJavascriptDirectory()
{
if (Directory.Exists(ServerPathMapper.MapPath(DefaultJsDir1)))
return DefaultJsDir1;
else if (Directory.Exists(ServerPathMapper.MapPath(DefaultJsDir2)))
return DefaultJsDir2;
else
return string.Empty;
}
private string GetDefaultRootStyleSheetDirectory()
{
if (Directory.Exists(ServerPathMapper.MapPath(DefaultCssDir)))
return DefaultCssDir;
return string.Empty;
}
private FileStreamResult StreamText(string inputText, string extension)
{
MemoryStream m = new MemoryStream(Encoding.Default.GetBytes(inputText));
return File(m, extension.Equals(CssExt) ? "text/css" : "text/javascript");
}
public static IPathMapper ServerPathMapper { get; set; }
public bool IsDebugMode
{
get
{
return ControllerContext.HttpContext.IsDebuggingEnabled;
}
}
public string JsExt
{
get
{
if (!IsDebugMode)
return string.Concat("min.", _jsExt);
else
return _jsExt;
}
}
public string CssExt
{
get
{
if (IsDebugMode)
return string.Concat("min.", _cssExt);
else
return _cssExt;
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment