mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-10-15 03:25:23 +08:00
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:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -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" />
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user