DO NOT DOWNLOAD. DOWNLOAD LATEST STABLE FROM RELEASE TAB
ClientDependency work [TFS Changeset #56528]
This commit is contained in:
@@ -6,6 +6,7 @@ using System.Web;
|
|||||||
using umbraco.presentation.ClientDependency.Providers;
|
using umbraco.presentation.ClientDependency.Providers;
|
||||||
using System.Web.Configuration;
|
using System.Web.Configuration;
|
||||||
using System.Configuration.Provider;
|
using System.Configuration.Provider;
|
||||||
|
using umbraco.presentation.ClientDependency.Config;
|
||||||
|
|
||||||
namespace umbraco.presentation.ClientDependency
|
namespace umbraco.presentation.ClientDependency
|
||||||
{
|
{
|
||||||
@@ -13,91 +14,6 @@ namespace umbraco.presentation.ClientDependency
|
|||||||
public class ClientDependencyHelper
|
public class ClientDependencyHelper
|
||||||
{
|
{
|
||||||
|
|
||||||
private static ClientDependencyProvider m_Provider = null;
|
|
||||||
private static ClientDependencyProviderCollection m_Providers = null;
|
|
||||||
private static List<string> m_Extensions;
|
|
||||||
private static object m_Lock = new object();
|
|
||||||
|
|
||||||
public static ClientDependencyProvider DefaultProvider
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
LoadProviders();
|
|
||||||
return m_Provider;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public static ClientDependencyProviderCollection ProviderCollection
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
LoadProviders();
|
|
||||||
return m_Providers;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The file extensions of Client Dependencies that are file based as opposed to request based.
|
|
||||||
/// Any file that doesn't have the extensions listed here will be request based, request based is
|
|
||||||
/// more overhead for the server to process.
|
|
||||||
/// </summary>
|
|
||||||
/// <example>
|
|
||||||
/// A request based JavaScript file may be a .ashx that dynamically creates JavaScript server side.
|
|
||||||
/// </example>
|
|
||||||
/// <remarks>
|
|
||||||
/// If this is not explicitly set, then the extensions 'js' and 'css' are the defaults.
|
|
||||||
/// </remarks>
|
|
||||||
public static List<string> FileBasedDependencyExtensionList
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
LoadProviders();
|
|
||||||
return m_Extensions;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void LoadProviders()
|
|
||||||
{
|
|
||||||
if (m_Provider == null)
|
|
||||||
{
|
|
||||||
lock (m_Lock)
|
|
||||||
{
|
|
||||||
// Do this again to make sure _provider is still null
|
|
||||||
if (m_Provider == null)
|
|
||||||
{
|
|
||||||
ClientDependencySection section = (ClientDependencySection)WebConfigurationManager.GetSection("system.web/clientDependency");
|
|
||||||
|
|
||||||
m_Providers = new ClientDependencyProviderCollection();
|
|
||||||
|
|
||||||
// if there is no section found, then add the standard providers to the collection with the standard
|
|
||||||
// default provider
|
|
||||||
if (section != null)
|
|
||||||
{
|
|
||||||
// Load registered providers and point _provider to the default provider
|
|
||||||
ProvidersHelper.InstantiateProviders(section.Providers, m_Providers, typeof(ClientDependencyProvider));
|
|
||||||
m_Provider = m_Providers[section.DefaultProvider];
|
|
||||||
if (m_Provider == null)
|
|
||||||
throw new ProviderException("Unable to load default ClientDependency provider");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//get the default settings
|
|
||||||
section = new ClientDependencySection();
|
|
||||||
m_Extensions = section.FileBasedDependencyExtensionList;
|
|
||||||
|
|
||||||
PageHeaderProvider php = new PageHeaderProvider();
|
|
||||||
php.Initialize(PageHeaderProvider.DefaultName, null);
|
|
||||||
m_Providers.Add(php);
|
|
||||||
ClientSideRegistrationProvider csrp = new ClientSideRegistrationProvider();
|
|
||||||
csrp.Initialize(ClientSideRegistrationProvider.DefaultName, null);
|
|
||||||
m_Providers.Add(csrp);
|
|
||||||
|
|
||||||
//set the default
|
|
||||||
m_Provider = m_Providers[section.DefaultProvider];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Registers dependencies with the default provider
|
/// Registers dependencies with the default provider
|
||||||
@@ -106,8 +22,8 @@ namespace umbraco.presentation.ClientDependency
|
|||||||
/// <param name="paths"></param>
|
/// <param name="paths"></param>
|
||||||
public static void RegisterClientDependencies(Control control, ClientDependencyPathCollection paths)
|
public static void RegisterClientDependencies(Control control, ClientDependencyPathCollection paths)
|
||||||
{
|
{
|
||||||
LoadProviders();
|
ClientDependencyRegistrationService service = new ClientDependencyRegistrationService(control, paths,
|
||||||
ClientDependencyRegistrationService service = new ClientDependencyRegistrationService(control, paths, m_Provider);
|
ClientDependencySettings.Instance.DefaultProvider);
|
||||||
service.ProcessDependencies();
|
service.ProcessDependencies();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,8 +35,8 @@ namespace umbraco.presentation.ClientDependency
|
|||||||
/// <param name="paths"></param>
|
/// <param name="paths"></param>
|
||||||
public static void RegisterClientDependencies(string providerName, Control control, ClientDependencyPathCollection paths)
|
public static void RegisterClientDependencies(string providerName, Control control, ClientDependencyPathCollection paths)
|
||||||
{
|
{
|
||||||
LoadProviders();
|
ClientDependencyRegistrationService service = new ClientDependencyRegistrationService(control, paths,
|
||||||
ClientDependencyRegistrationService service = new ClientDependencyRegistrationService(control, paths, m_Providers[providerName]);
|
ClientDependencySettings.Instance.ProviderCollection[providerName]);
|
||||||
service.ProcessDependencies();
|
service.ProcessDependencies();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,10 +46,9 @@ namespace umbraco.presentation.ClientDependency
|
|||||||
public static void RegisterClientDependencies<T>(Control control, ClientDependencyPathCollection paths)
|
public static void RegisterClientDependencies<T>(Control control, ClientDependencyPathCollection paths)
|
||||||
where T: ClientDependencyProvider
|
where T: ClientDependencyProvider
|
||||||
{
|
{
|
||||||
LoadProviders();
|
|
||||||
//need to find the provider with the type
|
//need to find the provider with the type
|
||||||
ClientDependencyProvider found = null;
|
ClientDependencyProvider found = null;
|
||||||
foreach (ClientDependencyProvider p in m_Providers)
|
foreach (ClientDependencyProvider p in ClientDependencySettings.Instance.ProviderCollection)
|
||||||
{
|
{
|
||||||
if (p.GetType().Equals(typeof(T)))
|
if (p.GetType().Equals(typeof(T)))
|
||||||
{
|
{
|
||||||
@@ -149,7 +64,6 @@ namespace umbraco.presentation.ClientDependency
|
|||||||
|
|
||||||
public static void RegisterClientDependencies(ClientDependencyProvider provider, Control control, ClientDependencyPathCollection paths)
|
public static void RegisterClientDependencies(ClientDependencyProvider provider, Control control, ClientDependencyPathCollection paths)
|
||||||
{
|
{
|
||||||
LoadProviders();
|
|
||||||
ClientDependencyRegistrationService service = new ClientDependencyRegistrationService(control, paths, provider);
|
ClientDependencyRegistrationService service = new ClientDependencyRegistrationService(control, paths, provider);
|
||||||
service.ProcessDependencies();
|
service.ProcessDependencies();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using System.Text;
|
|||||||
using System.Web.UI;
|
using System.Web.UI;
|
||||||
using System.Web;
|
using System.Web;
|
||||||
using umbraco.presentation.ClientDependency.Providers;
|
using umbraco.presentation.ClientDependency.Providers;
|
||||||
|
using umbraco.presentation.ClientDependency.Config;
|
||||||
|
|
||||||
namespace umbraco.presentation.ClientDependency
|
namespace umbraco.presentation.ClientDependency
|
||||||
{
|
{
|
||||||
@@ -69,12 +70,12 @@ namespace umbraco.presentation.ClientDependency
|
|||||||
switch (EmbedType)
|
switch (EmbedType)
|
||||||
{
|
{
|
||||||
case ClientDependencyEmbedType.Header:
|
case ClientDependencyEmbedType.Header:
|
||||||
provider = ClientDependencyHelper.ProviderCollection[PageHeaderProvider.DefaultName];
|
provider = ClientDependencySettings.Instance.ProviderCollection[PageHeaderProvider.DefaultName];
|
||||||
provider.IsDebugMode = IsDebugMode;
|
provider.IsDebugMode = IsDebugMode;
|
||||||
ClientDependencyHelper.RegisterClientDependencies(provider, this.Page, Paths);
|
ClientDependencyHelper.RegisterClientDependencies(provider, this.Page, Paths);
|
||||||
break;
|
break;
|
||||||
case ClientDependencyEmbedType.ClientSideRegistration:
|
case ClientDependencyEmbedType.ClientSideRegistration:
|
||||||
provider = ClientDependencyHelper.ProviderCollection[ClientSideRegistrationProvider.DefaultName];
|
provider = ClientDependencySettings.Instance.ProviderCollection[ClientSideRegistrationProvider.DefaultName];
|
||||||
provider.IsDebugMode = IsDebugMode;
|
provider.IsDebugMode = IsDebugMode;
|
||||||
ClientDependencyHelper.RegisterClientDependencies(provider, this.Page, Paths);
|
ClientDependencyHelper.RegisterClientDependencies(provider, this.Page, Paths);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ using System.Reflection;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.IO.Compression;
|
using System.IO.Compression;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
using System.Linq;
|
||||||
|
using umbraco.presentation.ClientDependency.Config;
|
||||||
|
|
||||||
namespace umbraco.presentation.ClientDependency
|
namespace umbraco.presentation.ClientDependency
|
||||||
{
|
{
|
||||||
@@ -64,25 +66,80 @@ namespace umbraco.presentation.ClientDependency
|
|||||||
if (string.IsNullOrEmpty(fileset))
|
if (string.IsNullOrEmpty(fileset))
|
||||||
throw new ArgumentException("Must specify a fileset in the request");
|
throw new ArgumentException("Must specify a fileset in the request");
|
||||||
|
|
||||||
byte[] fileBytes = CombineFiles(fileset, context, type);
|
string compositeFileName;
|
||||||
byte[] outputBytes = CompressBytes(context, fileBytes);
|
byte[] outputBytes;
|
||||||
SetCaching(context);
|
CompositeFileMap map = CompositeFileXmlMapper.Instance.GetCompositeFile(fileset);
|
||||||
|
if (map == null || !map.HasFileBytes)
|
||||||
|
{
|
||||||
|
//get the file list
|
||||||
|
string[] strFiles = DecodeFrom64(fileset).Split(';');
|
||||||
|
//combine files
|
||||||
|
byte[] fileBytes = CombineFiles(strFiles, context, type);
|
||||||
|
//compress data
|
||||||
|
CompressionType cType = CompressBytes(context, fileBytes, out outputBytes);
|
||||||
|
SetContentEncodingHeaders(context, cType);
|
||||||
|
//save combined file
|
||||||
|
compositeFileName = SaveCompositeFile(outputBytes, type);
|
||||||
|
|
||||||
|
//Update the XML file map
|
||||||
|
CompositeFileXmlMapper.Instance.CreateMap(fileset, cType.ToString(),
|
||||||
|
strFiles.Select(x => new FileInfo(context.Server.MapPath(x))).ToList(),
|
||||||
|
compositeFileName);
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//the saved file's bytes are already compressed.
|
||||||
|
outputBytes = map.GetCompositeFileBytes();
|
||||||
|
compositeFileName = map.CompositeFileName;
|
||||||
|
CompressionType cType = (CompressionType)Enum.Parse(typeof(CompressionType), map.CompressionType);
|
||||||
|
SetContentEncodingHeaders(context, cType);
|
||||||
|
}
|
||||||
|
|
||||||
|
SetCaching(context, compositeFileName);
|
||||||
|
|
||||||
context.Response.ContentType = type == ClientDependencyType.Javascript ? "text/javascript" : "text/css";
|
context.Response.ContentType = type == ClientDependencyType.Javascript ? "text/javascript" : "text/css";
|
||||||
context.Response.OutputStream.Write(outputBytes, 0, outputBytes.Length);
|
context.Response.OutputStream.Write(outputBytes, 0, outputBytes.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private enum CompressionType
|
||||||
|
{
|
||||||
|
deflate, gzip, none
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Saves the file's bytes to disk with a hash of the byte array
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="fileContents"></param>
|
||||||
|
/// <param name="type"></param>
|
||||||
|
/// <returns>The new file path</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// the extension will be: .cdj for JavaScript and .cdc for CSS
|
||||||
|
/// </remarks>
|
||||||
|
private string SaveCompositeFile(byte[] fileContents, ClientDependencyType type)
|
||||||
|
{
|
||||||
|
if (!ClientDependencySettings.Instance.CompositeFilePath.Exists)
|
||||||
|
ClientDependencySettings.Instance.CompositeFilePath.Create();
|
||||||
|
FileInfo fi = new FileInfo(
|
||||||
|
Path.Combine(ClientDependencySettings.Instance.CompositeFilePath.FullName,
|
||||||
|
fileContents.GetHashCode().ToString() + ".cd" + type.ToString().Substring(0, 1).ToLower()));
|
||||||
|
if (fi.Exists)
|
||||||
|
fi.Delete();
|
||||||
|
FileStream fs = fi.Create();
|
||||||
|
fs.Write(fileContents, 0, fileContents.Length);
|
||||||
|
fs.Close();
|
||||||
|
return fi.FullName;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// combines all files to a byte array
|
/// combines all files to a byte array
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="fileList"></param>
|
/// <param name="fileList"></param>
|
||||||
/// <param name="context"></param>
|
/// <param name="context"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private byte[] CombineFiles(string fileList, HttpContext context, ClientDependencyType type)
|
private byte[] CombineFiles(string[] strFiles, HttpContext context, ClientDependencyType type)
|
||||||
{
|
{
|
||||||
MemoryStream ms = new MemoryStream(5000);
|
MemoryStream ms = new MemoryStream(5000);
|
||||||
//get the file list, and write the contents of each file to the output stream.
|
|
||||||
string[] strFiles = DecodeFrom64(fileList).Split(';');
|
|
||||||
StreamWriter sw = new StreamWriter(ms);
|
StreamWriter sw = new StreamWriter(ms);
|
||||||
foreach (string s in strFiles)
|
foreach (string s in strFiles)
|
||||||
{
|
{
|
||||||
@@ -91,7 +148,7 @@ namespace umbraco.presentation.ClientDependency
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
FileInfo fi = new FileInfo(context.Server.MapPath(s));
|
FileInfo fi = new FileInfo(context.Server.MapPath(s));
|
||||||
if (ClientDependencyHelper.FileBasedDependencyExtensionList.Contains(fi.Extension.ToLower().Replace(".", "")))
|
if (ClientDependencySettings.Instance.FileBasedDependencyExtensionList.Contains(fi.Extension.ToLower().Replace(".", "")))
|
||||||
{
|
{
|
||||||
//if the file doesn't exist, then we'll assume it is a URI external request
|
//if the file doesn't exist, then we'll assume it is a URI external request
|
||||||
if (!fi.Exists)
|
if (!fi.Exists)
|
||||||
@@ -213,7 +270,7 @@ namespace umbraco.presentation.ClientDependency
|
|||||||
/// Sets the output cache parameters and also the client side caching parameters
|
/// Sets the output cache parameters and also the client side caching parameters
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context"></param>
|
/// <param name="context"></param>
|
||||||
private void SetCaching(HttpContext context)
|
private void SetCaching(HttpContext context, string fileName)
|
||||||
{
|
{
|
||||||
//This ensures OutputCaching is set for this handler and also controls
|
//This ensures OutputCaching is set for this handler and also controls
|
||||||
//client side caching on the browser side. Default is 10 days.
|
//client side caching on the browser side. Default is 10 days.
|
||||||
@@ -235,47 +292,68 @@ namespace umbraco.presentation.ClientDependency
|
|||||||
//This is the only way to set the max-age cachability header in ASP.Net!
|
//This is the only way to set the max-age cachability header in ASP.Net!
|
||||||
FieldInfo maxAgeField = cache.GetType().GetField("_maxAge", BindingFlags.Instance | BindingFlags.NonPublic);
|
FieldInfo maxAgeField = cache.GetType().GetField("_maxAge", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||||
maxAgeField.SetValue(cache, duration);
|
maxAgeField.SetValue(cache, duration);
|
||||||
|
|
||||||
|
//make this output cache dependent on the file.
|
||||||
|
context.Response.AddFileDependency(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the content encoding headers based on compressions
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
/// <param name="type"></param>
|
||||||
|
private void SetContentEncodingHeaders(HttpContext context, CompressionType type)
|
||||||
|
{
|
||||||
|
if (type == CompressionType.deflate)
|
||||||
|
{
|
||||||
|
context.Response.AddHeader("Content-encoding", "deflate");
|
||||||
|
}
|
||||||
|
else if (type == CompressionType.gzip)
|
||||||
|
{
|
||||||
|
context.Response.AddHeader("Content-encoding", "gzip");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Compresses the bytes if the browser supports it
|
/// Compresses the bytes if the browser supports it
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private byte[] CompressBytes(HttpContext context, byte[] fileBytes)
|
private CompressionType CompressBytes(HttpContext context, byte[] fileBytes, out byte[] outputBytes)
|
||||||
{
|
{
|
||||||
|
CompressionType type = CompressionType.none;
|
||||||
string acceptEncoding = context.Request.Headers["Accept-Encoding"];
|
string acceptEncoding = context.Request.Headers["Accept-Encoding"];
|
||||||
|
//not compressed initially
|
||||||
|
outputBytes = fileBytes;
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(acceptEncoding))
|
if (!string.IsNullOrEmpty(acceptEncoding))
|
||||||
{
|
{
|
||||||
MemoryStream ms = new MemoryStream();
|
MemoryStream ms = new MemoryStream();
|
||||||
Stream compressedStream = null;
|
Stream compressedStream = null;
|
||||||
acceptEncoding = acceptEncoding.ToLowerInvariant();
|
acceptEncoding = acceptEncoding.ToLowerInvariant();
|
||||||
bool isCompressed = false;
|
|
||||||
|
|
||||||
//deflate is faster in .Net according to Mads Kristensen (blogengine.net)
|
//deflate is faster in .Net according to Mads Kristensen (blogengine.net)
|
||||||
if (acceptEncoding.Contains("deflate"))
|
if (acceptEncoding.Contains("deflate"))
|
||||||
{
|
{
|
||||||
context.Response.AddHeader("Content-encoding", "deflate");
|
|
||||||
compressedStream = new DeflateStream(ms, CompressionMode.Compress, true);
|
compressedStream = new DeflateStream(ms, CompressionMode.Compress, true);
|
||||||
isCompressed = true;
|
type = CompressionType.deflate;
|
||||||
}
|
}
|
||||||
else if (acceptEncoding.Contains("gzip"))
|
else if (acceptEncoding.Contains("gzip"))
|
||||||
{
|
{
|
||||||
context.Response.AddHeader("Content-encoding", "gzip");
|
|
||||||
compressedStream = new GZipStream(ms, CompressionMode.Compress, true);
|
compressedStream = new GZipStream(ms, CompressionMode.Compress, true);
|
||||||
isCompressed = true;
|
type = CompressionType.gzip;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isCompressed)
|
if (type != CompressionType.none)
|
||||||
{
|
{
|
||||||
//write the bytes to the compressed stream
|
//write the bytes to the compressed stream
|
||||||
compressedStream.Write(fileBytes, 0, fileBytes.Length);
|
compressedStream.Write(fileBytes, 0, fileBytes.Length);
|
||||||
compressedStream.Close();
|
compressedStream.Close();
|
||||||
byte[] outputBytes = ms.ToArray();
|
byte[] output = ms.ToArray();
|
||||||
ms.Close();
|
ms.Close();
|
||||||
return outputBytes;
|
outputBytes = output;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//not compressed
|
|
||||||
return fileBytes;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string DecodeFrom64(string toDecode)
|
private string DecodeFrom64(string toDecode)
|
||||||
|
|||||||
69
umbraco/presentation.ClientDependency/CompositeFileMap.cs
Normal file
69
umbraco/presentation.ClientDependency/CompositeFileMap.cs
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Xml.Linq;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace umbraco.presentation.ClientDependency
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Deserialized structure of the XML stored in the map file
|
||||||
|
/// </summary>
|
||||||
|
public class CompositeFileMap
|
||||||
|
{
|
||||||
|
|
||||||
|
internal CompositeFileMap(string key, string compressionType, string file, List<FileInfo> files)
|
||||||
|
{
|
||||||
|
DependentFiles = files;
|
||||||
|
Base64Key = key;
|
||||||
|
CompositeFileName = file;
|
||||||
|
CompressionType = compressionType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Base64Key { get; private set; }
|
||||||
|
public string CompositeFileName { get; private set; }
|
||||||
|
public string CompressionType { get; private set; }
|
||||||
|
|
||||||
|
private byte[] m_FileBytes;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If for some reason the file doesn't exist any more or we cannot read the file, this will return false.
|
||||||
|
/// </summary>
|
||||||
|
public bool HasFileBytes
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
GetCompositeFileBytes();
|
||||||
|
return m_FileBytes != null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the file's bytes
|
||||||
|
/// </summary>
|
||||||
|
public byte[] GetCompositeFileBytes()
|
||||||
|
{
|
||||||
|
if (m_FileBytes == null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
FileInfo fi = new FileInfo(CompositeFileName);
|
||||||
|
FileStream fs = fi.OpenRead();
|
||||||
|
byte[] fileBytes = new byte[fs.Length];
|
||||||
|
fs.Read(fileBytes, 0, fileBytes.Length);
|
||||||
|
fs.Close();
|
||||||
|
m_FileBytes = fileBytes;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
m_FileBytes = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m_FileBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<FileInfo> DependentFiles { get; private set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
193
umbraco/presentation.ClientDependency/CompositeFileXmlMapper.cs
Normal file
193
umbraco/presentation.ClientDependency/CompositeFileXmlMapper.cs
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Xml.Linq;
|
||||||
|
using System.Xml;
|
||||||
|
using System.IO;
|
||||||
|
using umbraco.presentation.ClientDependency.Config;
|
||||||
|
|
||||||
|
namespace umbraco.presentation.ClientDependency
|
||||||
|
{
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates an XML file to map a saved composite file to the URL requested for the
|
||||||
|
/// dependency handler.
|
||||||
|
/// This is used in order to determine which individual files are dependant on what composite file so
|
||||||
|
/// a user can remove it to clear the cache, and also if the cache expires but the file still exists
|
||||||
|
/// this allows the system to simply read the one file again instead of compiling all of the other files
|
||||||
|
/// into one again.
|
||||||
|
/// </summary>
|
||||||
|
public class CompositeFileXmlMapper
|
||||||
|
{
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Singleton
|
||||||
|
/// </summary>
|
||||||
|
public static CompositeFileXmlMapper Instance
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return m_Mapper;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private CompositeFileXmlMapper()
|
||||||
|
{
|
||||||
|
Initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly CompositeFileXmlMapper m_Mapper = new CompositeFileXmlMapper();
|
||||||
|
|
||||||
|
private const string MapFileName = "map.xml";
|
||||||
|
|
||||||
|
private XDocument m_Doc;
|
||||||
|
private FileInfo m_XmlFile;
|
||||||
|
private object m_Lock = new object();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Loads in the existing file contents. If the file doesn't exist, it creates one.
|
||||||
|
/// </summary>
|
||||||
|
private void Initialize()
|
||||||
|
{
|
||||||
|
|
||||||
|
m_XmlFile = new FileInfo(
|
||||||
|
Path.Combine(ClientDependencySettings.Instance.CompositeFilePath.FullName, MapFileName));
|
||||||
|
|
||||||
|
EnsureXmlFile();
|
||||||
|
|
||||||
|
lock (m_Lock)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
m_Doc = XDocument.Load(m_XmlFile.FullName);
|
||||||
|
}
|
||||||
|
catch (XmlException ex)
|
||||||
|
{
|
||||||
|
//if it's an xml exception, create a new one and try one more time... should always work.
|
||||||
|
CreateNewXmlFile();
|
||||||
|
m_Doc = XDocument.Load(m_XmlFile.FullName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CreateNewXmlFile()
|
||||||
|
{
|
||||||
|
if (m_XmlFile.Exists)
|
||||||
|
m_XmlFile.Delete();
|
||||||
|
|
||||||
|
m_Doc = new XDocument(new XDeclaration("1.0", "UTF-8", "yes"),
|
||||||
|
new XElement("map"));
|
||||||
|
m_Doc.Save(m_XmlFile.FullName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EnsureXmlFile()
|
||||||
|
{
|
||||||
|
if (!m_XmlFile.Exists)
|
||||||
|
{
|
||||||
|
lock (m_Lock)
|
||||||
|
{
|
||||||
|
//double check
|
||||||
|
if (!m_XmlFile.Exists)
|
||||||
|
{
|
||||||
|
CreateNewXmlFile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the composite file map associated with the base 64 key of the URL
|
||||||
|
/// for the handler.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="base64Key"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public CompositeFileMap GetCompositeFile(string base64Key)
|
||||||
|
{
|
||||||
|
XElement x = FindItem(base64Key);
|
||||||
|
//try
|
||||||
|
//{
|
||||||
|
return (x == null ? null : new CompositeFileMap(base64Key,
|
||||||
|
x.Attribute("compression").Value,
|
||||||
|
x.Attribute("file").Value,
|
||||||
|
x.Descendants("file")
|
||||||
|
.Select(f => new FileInfo(f.Attribute("name").Value))
|
||||||
|
.ToList()));
|
||||||
|
//}
|
||||||
|
//catch
|
||||||
|
//{
|
||||||
|
// return null;
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="base64Key"></param>
|
||||||
|
/// <param name="dependentFiles"></param>
|
||||||
|
/// <param name="compositeFile"></param>
|
||||||
|
/// <example>
|
||||||
|
/// <![CDATA[
|
||||||
|
/// <map>
|
||||||
|
/// <item key="XSDFSDKJHLKSDIOUEYWCDCDSDOIUPOIUEROIJDSFHG"
|
||||||
|
/// file="C:\asdf\App_Data\ClientDependency\123456.cdj"
|
||||||
|
/// compresion="deflate">
|
||||||
|
/// <files>
|
||||||
|
/// <file name="C:\asdf\JS\jquery.js" />
|
||||||
|
/// <file name="C:\asdf\JS\jquery.ui.js" />
|
||||||
|
/// </files>
|
||||||
|
/// </item>
|
||||||
|
/// </map>
|
||||||
|
/// ]]>
|
||||||
|
/// </example>
|
||||||
|
public void CreateMap(string base64Key, string compressionType, List<FileInfo> dependentFiles, string compositeFile)
|
||||||
|
{
|
||||||
|
lock (m_Lock)
|
||||||
|
{
|
||||||
|
//see if we can find an item with the key already
|
||||||
|
XElement x = FindItem(base64Key);
|
||||||
|
|
||||||
|
if (x != null)
|
||||||
|
{
|
||||||
|
x.Attribute("file").Value = compositeFile;
|
||||||
|
//remove all of the files so we can re-add them.
|
||||||
|
x.Element("files").Remove();
|
||||||
|
|
||||||
|
x.Add(CreateFileNode(dependentFiles));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//if it doesn't exist, create it
|
||||||
|
m_Doc.Root.Add(new XElement("item",
|
||||||
|
new XAttribute("key", base64Key),
|
||||||
|
new XAttribute("file", compositeFile),
|
||||||
|
new XAttribute("compression", compressionType),
|
||||||
|
CreateFileNode(dependentFiles)));
|
||||||
|
}
|
||||||
|
|
||||||
|
m_Doc.Save(m_XmlFile.FullName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private XElement FindItem(string key)
|
||||||
|
{
|
||||||
|
return m_Doc.Root.Elements("item")
|
||||||
|
.Where(e => (string)e.Attribute("key") == key)
|
||||||
|
.SingleOrDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
private XElement CreateFileNode(List<FileInfo> files)
|
||||||
|
{
|
||||||
|
XElement x = new XElement("files");
|
||||||
|
|
||||||
|
//add all of the files
|
||||||
|
files.ForEach(d =>
|
||||||
|
{
|
||||||
|
x.Add(new XElement("file",
|
||||||
|
new XAttribute("name", d.FullName)));
|
||||||
|
});
|
||||||
|
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@ using System.Text;
|
|||||||
using System.Configuration;
|
using System.Configuration;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace umbraco.presentation.ClientDependency.Providers
|
namespace umbraco.presentation.ClientDependency.Config
|
||||||
{
|
{
|
||||||
public class ClientDependencySection : ConfigurationSection
|
public class ClientDependencySection : ConfigurationSection
|
||||||
{
|
{
|
||||||
@@ -22,6 +22,16 @@ namespace umbraco.presentation.ClientDependency.Providers
|
|||||||
set { base["defaultProvider"] = value; }
|
set { base["defaultProvider"] = value; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This is the folder that composite script/css files will be stored once they are combined.
|
||||||
|
/// </summary>
|
||||||
|
[StringValidator(MinLength = 1)]
|
||||||
|
[ConfigurationProperty("compositeFilePath", DefaultValue = "~/App_Data/ClientDependency")]
|
||||||
|
public string CompositeFilePath
|
||||||
|
{
|
||||||
|
get { return (string)base["compositeFilePath"]; }
|
||||||
|
set { base["compositeFilePath"] = value; }
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The configuration section to set the FileBasedDependencyExtensionList. This is a comma separated list.
|
/// The configuration section to set the FileBasedDependencyExtensionList. This is a comma separated list.
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Web.Configuration;
|
||||||
|
using umbraco.presentation.ClientDependency.Providers;
|
||||||
|
using System.Configuration.Provider;
|
||||||
|
using System.IO;
|
||||||
|
using System.Web;
|
||||||
|
|
||||||
|
namespace umbraco.presentation.ClientDependency.Config
|
||||||
|
{
|
||||||
|
public class ClientDependencySettings
|
||||||
|
{
|
||||||
|
|
||||||
|
private ClientDependencySettings()
|
||||||
|
{
|
||||||
|
LoadProviders();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Singleton
|
||||||
|
/// </summary>
|
||||||
|
public static ClientDependencySettings Instance
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return m_Settings;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly ClientDependencySettings m_Settings = new ClientDependencySettings();
|
||||||
|
|
||||||
|
private object m_Lock = new object();
|
||||||
|
private ClientDependencyProvider m_Provider = null;
|
||||||
|
private ClientDependencyProviderCollection m_Providers = null;
|
||||||
|
private List<string> m_Extensions;
|
||||||
|
private DirectoryInfo m_CompositeFilePath;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The file extensions of Client Dependencies that are file based as opposed to request based.
|
||||||
|
/// Any file that doesn't have the extensions listed here will be request based, request based is
|
||||||
|
/// more overhead for the server to process.
|
||||||
|
/// </summary>
|
||||||
|
/// <example>
|
||||||
|
/// A request based JavaScript file may be a .ashx that dynamically creates JavaScript server side.
|
||||||
|
/// </example>
|
||||||
|
/// <remarks>
|
||||||
|
/// If this is not explicitly set, then the extensions 'js' and 'css' are the defaults.
|
||||||
|
/// </remarks>
|
||||||
|
public List<string> FileBasedDependencyExtensionList
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return m_Extensions;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClientDependencyProvider DefaultProvider
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return m_Provider;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public ClientDependencyProviderCollection ProviderCollection
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return m_Providers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public DirectoryInfo CompositeFilePath
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return m_CompositeFilePath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadProviders()
|
||||||
|
{
|
||||||
|
if (m_Provider == null)
|
||||||
|
{
|
||||||
|
lock (m_Lock)
|
||||||
|
{
|
||||||
|
// Do this again to make sure _provider is still null
|
||||||
|
if (m_Provider == null)
|
||||||
|
{
|
||||||
|
ClientDependencySection section = (ClientDependencySection)WebConfigurationManager.GetSection("system.web/clientDependency");
|
||||||
|
|
||||||
|
m_Providers = new ClientDependencyProviderCollection();
|
||||||
|
|
||||||
|
// if there is no section found, then add the standard providers to the collection with the standard
|
||||||
|
// default provider
|
||||||
|
if (section != null)
|
||||||
|
{
|
||||||
|
// Load registered providers and point _provider to the default provider
|
||||||
|
ProvidersHelper.InstantiateProviders(section.Providers, m_Providers, typeof(ClientDependencyProvider));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//create a new section with the default settings
|
||||||
|
section = new ClientDependencySection();
|
||||||
|
PageHeaderProvider php = new PageHeaderProvider();
|
||||||
|
php.Initialize(PageHeaderProvider.DefaultName, null);
|
||||||
|
m_Providers.Add(php);
|
||||||
|
ClientSideRegistrationProvider csrp = new ClientSideRegistrationProvider();
|
||||||
|
csrp.Initialize(ClientSideRegistrationProvider.DefaultName, null);
|
||||||
|
m_Providers.Add(csrp);
|
||||||
|
}
|
||||||
|
|
||||||
|
//set the default
|
||||||
|
m_Provider = m_Providers[section.DefaultProvider];
|
||||||
|
if (m_Provider == null)
|
||||||
|
throw new ProviderException("Unable to load default ClientDependency provider");
|
||||||
|
|
||||||
|
m_Extensions = section.FileBasedDependencyExtensionList;
|
||||||
|
m_CompositeFilePath = new DirectoryInfo(HttpContext.Current.Server.MapPath(section.CompositeFilePath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -46,11 +46,17 @@
|
|||||||
<Reference Include="System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />
|
<Reference Include="System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />
|
||||||
<Reference Include="System.Data" />
|
<Reference Include="System.Data" />
|
||||||
<Reference Include="System.Xml" />
|
<Reference Include="System.Xml" />
|
||||||
|
<Reference Include="System.Xml.Linq">
|
||||||
|
<RequiredTargetFramework>3.5</RequiredTargetFramework>
|
||||||
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="ClientDependencyInclude.cs" />
|
<Compile Include="ClientDependencyInclude.cs" />
|
||||||
<Compile Include="ClientDependencyList.cs" />
|
<Compile Include="ClientDependencyList.cs" />
|
||||||
<Compile Include="ClientDependencyType.cs" />
|
<Compile Include="ClientDependencyType.cs" />
|
||||||
|
<Compile Include="CompositeFileMap.cs" />
|
||||||
|
<Compile Include="CompositeFileXmlMapper.cs" />
|
||||||
|
<Compile Include="Config\ClientDependencySettings.cs" />
|
||||||
<Compile Include="IClientDependencyFile.cs" />
|
<Compile Include="IClientDependencyFile.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="Providers\ClientDependencyProvider.cs" />
|
<Compile Include="Providers\ClientDependencyProvider.cs" />
|
||||||
@@ -61,7 +67,7 @@
|
|||||||
<Compile Include="ClientDependencyPath.cs" />
|
<Compile Include="ClientDependencyPath.cs" />
|
||||||
<Compile Include="ClientDependencyPathCollection.cs" />
|
<Compile Include="ClientDependencyPathCollection.cs" />
|
||||||
<Compile Include="Providers\ClientDependencyProviderCollection.cs" />
|
<Compile Include="Providers\ClientDependencyProviderCollection.cs" />
|
||||||
<Compile Include="Providers\ClientDependencySection.cs" />
|
<Compile Include="Config\ClientDependencySection.cs" />
|
||||||
<Compile Include="Providers\ClientSideRegistrationProvider.cs" />
|
<Compile Include="Providers\ClientSideRegistrationProvider.cs" />
|
||||||
<Compile Include="CompositeDependencyHandler.cs" />
|
<Compile Include="CompositeDependencyHandler.cs" />
|
||||||
<Compile Include="Providers\PageHeaderProvider.cs" />
|
<Compile Include="Providers\PageHeaderProvider.cs" />
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// <auto-generated>
|
// <auto-generated>
|
||||||
// This code was generated by a tool.
|
// This code was generated by a tool.
|
||||||
// Runtime Version:2.0.50727.3053
|
// Runtime Version:2.0.50727.3082
|
||||||
//
|
//
|
||||||
// Changes to this file may cause incorrect behavior and will be lost if
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
// the code is regenerated.
|
// the code is regenerated.
|
||||||
|
|||||||
Reference in New Issue
Block a user