Continued work on package prototyping

Renamed and organized some components
Rough implementation of install/expand (currently to alternate location to avoid overwrites)

--HG--
branch : dev
This commit is contained in:
Louis DeJardin
2010-07-06 18:56:55 -07:00
parent 25c35ece69
commit 5c45c0d239
25 changed files with 802 additions and 185 deletions

View File

@@ -0,0 +1,21 @@
using System.ServiceModel.Syndication;
using System.Web.Mvc;
using System.Xml;
namespace PackageIndexReferenceImplementation.Controllers.Artifacts {
public class AtomFeedResult : ActionResult {
public SyndicationFeed Feed { get; set; }
public AtomFeedResult(SyndicationFeed feed) {
Feed = feed;
}
public override void ExecuteResult(ControllerContext context) {
context.HttpContext.Response.ContentType = "application/atom+xml";
using (var writer = XmlWriter.Create(context.HttpContext.Response.OutputStream)) {
var formatter = new Atom10FeedFormatter(Feed);
formatter.WriteTo(writer);
}
}
}
}

View File

@@ -0,0 +1,27 @@
using System.ServiceModel.Syndication;
using System.Web.Mvc;
using System.Xml;
namespace PackageIndexReferenceImplementation.Controllers.Artifacts {
public class AtomItemResult : ActionResult {
public string Status { get; set; }
public string Location { get; set; }
public SyndicationItem Item { get; set; }
public AtomItemResult(string status, string location, SyndicationItem item) {
Status = status;
Location = location;
Item = item;
}
public override void ExecuteResult(ControllerContext context) {
context.HttpContext.Response.Status = Status;
context.HttpContext.Response.RedirectLocation = Location;
context.HttpContext.Response.ContentType = "application/atom+xml;type=entry";
using (var writer = XmlWriter.Create(context.HttpContext.Response.OutputStream)) {
var formatter = new Atom10ItemFormatter(Item);
formatter.WriteTo(writer);
}
}
}
}

View File

@@ -0,0 +1,18 @@
using System;
using System.Reflection;
using System.Web.Mvc;
namespace PackageIndexReferenceImplementation.Controllers.Artifacts {
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class ContentTypeAttribute : ActionMethodSelectorAttribute {
public ContentTypeAttribute(string contentType) {
ContentType = contentType;
}
public string ContentType { get; set; }
public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo) {
return controllerContext.HttpContext.Request.ContentType.StartsWith(ContentType);
}
}
}

View File

@@ -0,0 +1,13 @@
using System;
using System.Web.Mvc;
using System.Xml.Linq;
namespace PackageIndexReferenceImplementation.Controllers.Artifacts {
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class XmlBodyAttribute : ActionFilterAttribute {
public override void OnActionExecuting(ActionExecutingContext filterContext) {
var body = XElement.Load(filterContext.HttpContext.Request.InputStream);
filterContext.ActionParameters["body"] = body.ToString();
}
}
}

View File

@@ -1,42 +1,27 @@
using System;
using System.IO;
using System.IO.Packaging;
using System.Reflection;
using System.Linq;
using System.Security.Policy;
using System.Web.Mvc;
using System.Xml;
using System.Xml.Linq;
using System.Web.Routing;
using System.ServiceModel.Syndication;
using PackageIndexReferenceImplementation.Controllers.Artifacts;
using PackageIndexReferenceImplementation.Services;
namespace PackageIndexReferenceImplementation.Controllers {
public class SyndicationResult : ActionResult {
public SyndicationFeedFormatter Formatter { get; set; }
public SyndicationResult(SyndicationFeedFormatter formatter) {
Formatter = formatter;
}
public override void ExecuteResult(ControllerContext context) {
context.HttpContext.Response.ContentType = "application/atom+xml";
using (var writer = XmlWriter.Create(context.HttpContext.Response.OutputStream)) {
Formatter.WriteTo(writer);
}
}
}
[HandleError]
public class AtomController : Controller {
public ActionResult Index() {
var feed = new SyndicationFeed {
Items = new[] {
new SyndicationItem {
Id = "hello",
Title = new TextSyndicationContent("Orchard.Media", TextSyndicationContentKind.Plaintext),
LastUpdatedTime = DateTimeOffset.UtcNow
}
}
};
private readonly FeedStorage _feedStorage;
private readonly MediaStorage _mediaStorage;
return new SyndicationResult(new Atom10FeedFormatter(feed));
public AtomController() {
_feedStorage = new FeedStorage();
_mediaStorage = new MediaStorage();
}
public ActionResult Index() {
return new AtomFeedResult(_feedStorage.GetFeed());
}
[ActionName("Index"), HttpPost, ContentType("application/atom+xml"), XmlBody]
@@ -46,37 +31,68 @@ namespace PackageIndexReferenceImplementation.Controllers {
[ActionName("Index"), HttpPost, ContentType("application/x-package")]
public ActionResult PostPackage() {
var hostHeader = HttpContext.Request.Headers["Host"];
var slugHeader = HttpContext.Request.Headers["Slug"];
var utcNowDateString = DateTimeOffset.UtcNow.ToString("yyyy-MM-dd");
var package = Package.Open(Request.InputStream, FileMode.Open, FileAccess.Read);
var packageProperties = package.PackageProperties;
return RedirectToAction("Index");
var feed = _feedStorage.GetFeed();
var item = feed.Items.FirstOrDefault(i => i.Id.StartsWith("tag:") && i.Id.EndsWith(":" + packageProperties.Identifier));
if (item == null) {
item = new SyndicationItem {
Id = "tag:" + hostHeader + "," + utcNowDateString + ":" + packageProperties.Identifier
};
feed.Items = feed.Items.Concat(new[] { item });
}
if (!string.IsNullOrEmpty(packageProperties.Category)) {
item.Authors.Clear();
//parse package.PackageProperties.Creator into email-style authors
item.Authors.Add(new SyndicationPerson { Name = packageProperties.Creator });
}
if (!string.IsNullOrEmpty(packageProperties.Category)) {
item.Categories.Clear();
item.Categories.Add(new SyndicationCategory(packageProperties.Category));
}
if (packageProperties.Modified.HasValue) {
item.LastUpdatedTime = new DateTimeOffset(packageProperties.Modified.Value);
}
if (!string.IsNullOrEmpty(packageProperties.Title)) {
item.Title = new TextSyndicationContent(packageProperties.Title);
}
if (!string.IsNullOrEmpty(packageProperties.Description)) {
item.Summary = new TextSyndicationContent(packageProperties.Description);
}
if (!string.IsNullOrEmpty(packageProperties.Title)) {
item.Title = new TextSyndicationContent(packageProperties.Title);
}
var mediaIdentifier = packageProperties.Identifier + "-" + packageProperties.Version + ".zip";
var mediaUrl = Url.Action("Resource", "Media", new RouteValueDictionary { { "Id", mediaIdentifier }, { "ContentType", "application/x-package" } });
item.Links.Clear();
item.Links.Add(new SyndicationLink(new Uri(HostBaseUri(), new Uri(mediaUrl, UriKind.Relative))));
Request.InputStream.Seek(0, SeekOrigin.Begin);
_mediaStorage.StoreMedia(mediaIdentifier+":application/x-package", Request.InputStream);
_feedStorage.StoreFeed(feed);
return new AtomItemResult("201 Created", null, item);
}
static XElement Atom(string localName, params XNode[] content) {
return new XElement(XName.Get(localName, "http://www.w3.org/2005/Atom"), content);
}
static XElement Atom(string localName, string value) {
return new XElement(XName.Get(localName, "http://www.w3.org/2005/Atom"), new XText(value));
}
}
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class ContentTypeAttribute : ActionMethodSelectorAttribute {
public ContentTypeAttribute(string contentType) {
ContentType = contentType;
private Uri HostBaseUri() {
return new Uri("http://" + HttpContext.Request.Headers["Host"]);
}
public string ContentType { get; set; }
public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo) {
return controllerContext.HttpContext.Request.ContentType.StartsWith(ContentType);
}
}
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class XmlBodyAttribute : ActionFilterAttribute {
public override void OnActionExecuting(ActionExecutingContext filterContext) {
var body = XElement.Load(filterContext.HttpContext.Request.InputStream);
filterContext.ActionParameters["body"] = body.ToString();
}
}
}

View File

@@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using PackageIndexReferenceImplementation.Services;
namespace PackageIndexReferenceImplementation.Controllers
{
public class MediaController : Controller
{
private readonly MediaStorage _mediaStorage;
public MediaController() {
_mediaStorage = new MediaStorage();
}
public ActionResult Resource(string id, string contentType)
{
return new StreamResult(contentType, _mediaStorage.GetMedia(id + ":" + contentType));
}
}
public class StreamResult : ActionResult {
public string ContentType { get; set; }
public Stream Stream { get; set; }
public StreamResult(string contentType, Stream stream) {
ContentType = contentType;
Stream = stream;
}
public override void ExecuteResult(ControllerContext context) {
context.HttpContext.Response.ContentType = ContentType;
Stream.CopyTo(context.HttpContext.Response.OutputStream);
}
}
}

View File

@@ -69,12 +69,19 @@
<ItemGroup>
<Compile Include="Controllers\AccountController.cs" />
<Compile Include="Controllers\AtomController.cs" />
<Compile Include="Controllers\Artifacts\AtomFeedResult.cs" />
<Compile Include="Controllers\Artifacts\AtomItemResult.cs" />
<Compile Include="Controllers\Artifacts\ContentTypeAttribute.cs" />
<Compile Include="Controllers\HomeController.cs" />
<Compile Include="Controllers\MediaController.cs" />
<Compile Include="Controllers\Artifacts\XmlBodyAttribute.cs" />
<Compile Include="Global.asax.cs">
<DependentUpon>Global.asax</DependentUpon>
</Compile>
<Compile Include="Models\AccountModels.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Services\MediaStorage.cs" />
<Compile Include="Services\FeedStorage.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="Global.asax" />

View File

@@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.ServiceModel.Syndication;
using System.Web;
using System.Web.Hosting;
using System.Xml;
namespace PackageIndexReferenceImplementation.Services {
public class FeedStorage {
public SyndicationFeed GetFeed() {
var formatter = new Atom10FeedFormatter<SyndicationFeed>();
var feedPath = HostingEnvironment.MapPath("~/App_Data/Feed.xml");
if (!File.Exists(feedPath)) {
return new SyndicationFeed();
}
using (var reader = XmlReader.Create(feedPath)) {
formatter.ReadFrom(reader);
return formatter.Feed;
}
}
public void StoreFeed(SyndicationFeed feed) {
var formatter = new Atom10FeedFormatter<SyndicationFeed>(feed);
var feedPath = HostingEnvironment.MapPath("~/App_Data/Feed.xml");
using (var writer = XmlWriter.Create(feedPath)) {
formatter.WriteTo(writer);
}
}
}
}

View File

@@ -0,0 +1,36 @@
using System.IO;
using System.Linq;
using System.Web.Hosting;
namespace PackageIndexReferenceImplementation.Services {
public class MediaStorage {
public void StoreMedia(string identifier, Stream data) {
if (!Directory.Exists(HostingEnvironment.MapPath("~/App_Data/Media")))
Directory.CreateDirectory(HostingEnvironment.MapPath("~/App_Data/Media"));
var safeIdentifier = GetSafeIdentifier(identifier);
var filePath = HostingEnvironment.MapPath("~/App_Data/Media/" + safeIdentifier);
using (var destination = new FileStream(filePath, FileMode.Create, FileAccess.Write)) {
data.CopyTo(destination);
}
}
public Stream GetMedia(string identifier) {
if (!Directory.Exists(HostingEnvironment.MapPath("~/App_Data/Media")))
Directory.CreateDirectory(HostingEnvironment.MapPath("~/App_Data/Media"));
var safeIdentifier = GetSafeIdentifier(identifier);
var filePath = HostingEnvironment.MapPath("~/App_Data/Media/" + safeIdentifier);
return new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);
}
static string GetSafeIdentifier(string identifier) {
var invalidFileNameChars = Path.GetInvalidFileNameChars().Concat(Path.GetInvalidPathChars()).Distinct();
var safeIdentifier = identifier.Replace("^", string.Format("^{0:X2}", (int)'^'));
foreach (var ch in invalidFileNameChars) {
safeIdentifier = safeIdentifier.Replace(new string(ch, 1), string.Format("^{0:X2}", (int)ch));
}
return safeIdentifier;
}
}
}