Files
Orchard/src/Orchard.Web/Modules/Orchard.Packaging/Services/PackageExpander.cs
2010-10-04 14:52:46 -07:00

203 lines
8.3 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Packaging;
using System.Linq;
using System.Reflection;
using System.Xml.Linq;
using Orchard.Environment.Extensions;
using Orchard.FileSystems.VirtualPath;
using Orchard.Localization;
namespace Orchard.Packaging.Services {
[OrchardFeature("PackagingServices")]
public class PackageExpander : IPackageExpander {
private const string ContentTypePrefix = "Orchard ";
private readonly IVirtualPathProvider _virtualPathProvider;
public PackageExpander(IVirtualPathProvider virtualPathProvider) {
_virtualPathProvider = virtualPathProvider;
T = NullLocalizer.Instance;
}
public Localizer T { get; set; }
#region IPackageExpander Members
public PackageInfo ExpandPackage(Stream packageStream) {
var context = new ExpandContext();
BeginPackage(context, packageStream);
try {
GetCoreProperties(context);
EstablishPaths(context, _virtualPathProvider);
string projectFile = context.ExtensionName + ".csproj";
if (LoadProject(context, projectFile)) {
ExtractFile(context, projectFile);
ExtractProjectFiles(context, "Compile", "Content", "None", "EmbeddedResource");
ExtractReferenceFiles(context);
}
else if (context.ExtensionType == "Theme") {
// this is a simple theme with no csproj
ExtractThemeFiles(context);
}
}
finally {
EndPackage(context);
}
return new PackageInfo {
ExtensionName = context.ExtensionName,
ExtensionVersion = context.ExtensionVersion,
ExtensionType = context.ExtensionType,
ExtensionPath = context.TargetPath
};
}
#endregion
private void ExtractFile(ExpandContext context, string relativePath) {
Uri partUri = PackUriHelper.CreatePartUri(new Uri(context.SourcePath + relativePath, UriKind.Relative));
PackagePart packagePart = context.Package.GetPart(partUri);
using (Stream packageStream = packagePart.GetStream(FileMode.Open, FileAccess.Read)) {
string filePath = _virtualPathProvider.Combine(context.TargetPath, relativePath);
string folderPath = _virtualPathProvider.GetDirectoryName(filePath);
if (!_virtualPathProvider.DirectoryExists(folderPath)) {
_virtualPathProvider.CreateDirectory(folderPath);
}
using (Stream fileStream = _virtualPathProvider.CreateFile(filePath)) {
packageStream.CopyTo(fileStream);
}
}
}
private void ExtractProjectFiles(ExpandContext context, params string[] itemGroupTypes) {
IEnumerable<XElement> itemGroups = context.Project
.Elements(Ns("Project"))
.Elements(Ns("ItemGroup"));
foreach (string itemGroupType in itemGroupTypes) {
IEnumerable<string> includePaths = itemGroups
.Elements(Ns(itemGroupType))
.Attributes("Include")
.Select(x => x.Value);
foreach (string includePath in includePaths) {
ExtractFile(context, includePath);
}
}
}
private void ExtractReferenceFiles(ExpandContext context) {
var entries = context.Project
.Elements(Ns("Project"))
.Elements(Ns("ItemGroup"))
.Elements(Ns("Reference"))
.Select(reference => new {
Include = reference.Attribute("Include"),
HintPath = reference.Element(Ns("HintPath"))
})
.Where(entry => entry.Include != null);
foreach (var entry in entries) {
var assemblyName = new AssemblyName(entry.Include.Value);
string hintPath = entry.HintPath != null ? entry.HintPath.Value : null;
string virtualPath = "bin/" + assemblyName.Name + ".dll";
if (PartExists(context, virtualPath)) {
ExtractFile(context, virtualPath);
}
else if (hintPath != null) {}
}
}
private void ExtractThemeFiles(ExpandContext context) {
foreach (var relativePath in from p in context.Package.GetParts()
where p.Uri.ToString().StartsWith("/" + context.ExtensionName + "/", StringComparison.OrdinalIgnoreCase)
select p.Uri.ToString().Substring(("/" + context.ExtensionName + "/").Length)) {
ExtractFile(context, relativePath);
}
}
private bool PartExists(ExpandContext context, string relativePath) {
Uri projectUri = PackUriHelper.CreatePartUri(new Uri(context.SourcePath + relativePath, UriKind.Relative));
return context.Package.PartExists(projectUri);
}
private XName Ns(string localName) {
return XName.Get(localName, "http://schemas.microsoft.com/developer/msbuild/2003");
}
private static bool LoadProject(ExpandContext context, string relativePath) {
Uri projectUri = PackUriHelper.CreatePartUri(new Uri(context.SourcePath + relativePath, UriKind.Relative));
if (!context.Package.PartExists(projectUri)) {
return false;
}
PackagePart part = context.Package.GetPart(projectUri);
using (Stream stream = part.GetStream(FileMode.Open, FileAccess.Read)) {
context.Project = XDocument.Load(stream);
}
return true;
}
private void BeginPackage(ExpandContext context, Stream packageStream) {
if (packageStream.CanSeek) {
context.Stream = packageStream;
}
else {
context.Stream = new MemoryStream();
packageStream.CopyTo(context.Stream);
}
context.Package = Package.Open(context.Stream, FileMode.Open, FileAccess.Read);
}
private void EndPackage(ExpandContext context) {
context.Package.Close();
}
private void GetCoreProperties(ExpandContext context) {
context.ExtensionName = context.Package.PackageProperties.Identifier;
context.ExtensionVersion = context.Package.PackageProperties.Version;
string contentType = context.Package.PackageProperties.ContentType;
if (contentType.StartsWith(ContentTypePrefix)) {
context.ExtensionType = contentType.Substring(ContentTypePrefix.Length);
}
}
private void EstablishPaths(ExpandContext context, IVirtualPathProvider virtualPathProvider) {
context.SourcePath = "\\" + context.ExtensionName + "\\";
switch (context.ExtensionType) {
case "Theme":
context.TargetPath = virtualPathProvider.Combine("~/Themes/" + context.ExtensionName);
break;
case "Module":
context.TargetPath = virtualPathProvider.Combine("~/Modules/" + context.ExtensionName);
break;
default:
throw new OrchardCoreException(T("Unknown extension type \"{0}\"", context.ExtensionType));
}
}
#region Nested type: ExpandContext
private class ExpandContext {
public Stream Stream { get; set; }
public Package Package { get; set; }
public string ExtensionName { get; set; }
public string ExtensionVersion { get; set; }
public string ExtensionType { get; set; }
public string TargetPath { get; set; }
public string SourcePath { get; set; }
public XDocument Project { get; set; }
}
#endregion
}
}