Groundwork for rss support by container id

--HG--
extra : convert_revision : svn%3A5ff7c347-ad56-4c35-b696-ccb81de16e03/trunk%4045452
This commit is contained in:
loudej
2010-01-15 03:44:54 +00:00
parent 775567683c
commit c4eaca47e5
19 changed files with 607 additions and 0 deletions

View File

@@ -0,0 +1,57 @@
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using JetBrains.Annotations;
using Orchard.Core.Feeds.Models;
using Orchard.Logging;
using Orchard.Mvc.Results;
namespace Orchard.Core.Feeds.Controllers {
public class FeedController : Controller {
private readonly IEnumerable<IFeedFormatterProvider> _feedFormatProviders;
private readonly IEnumerable<IFeedQueryProvider> _feedQueryProviders;
private readonly IEnumerable<IFeedItemBuilder> _feedItemBuilders;
public FeedController(
IEnumerable<IFeedQueryProvider> feedQueryProviders,
IEnumerable<IFeedFormatterProvider> feedFormatProviders,
IEnumerable<IFeedItemBuilder> feedItemBuilders) {
_feedQueryProviders = feedQueryProviders;
_feedFormatProviders = feedFormatProviders;
_feedItemBuilders = feedItemBuilders;
Logger = NullLogger.Instance;
}
public ILogger Logger { get; set; }
public ActionResult Index(string format) {
var context = new FeedContext(ValueProvider, format);
var bestFormatterMatch = _feedFormatProviders
.Select(provider => provider.Match(context))
.Where(match => match != null && match.FeedFormatter != null)
.OrderByDescending(match => match.Priority)
.FirstOrDefault();
if (bestFormatterMatch == null || bestFormatterMatch.FeedFormatter == null)
return new NotFoundResult();
context.FeedFormatter = bestFormatterMatch.FeedFormatter;
var bestQueryMatch = _feedQueryProviders
.Select(provider => provider.Match(context))
.Where(match => match != null && match.FeedQuery != null)
.OrderByDescending(match => match.Priority)
.FirstOrDefault();
if (bestQueryMatch == null || bestQueryMatch.FeedQuery == null)
return new NotFoundResult();
return context.FeedFormatter.Process(context, () => {
bestQueryMatch.FeedQuery.Execute(context);
_feedItemBuilders.Invoke(x => x.Populate(context), Logger);
});
}
}
}

View File

@@ -0,0 +1,13 @@
using System;
using System.Web.Mvc;
using Orchard.ContentManagement;
using Orchard.Core.Feeds.Models;
namespace Orchard.Core.Feeds {
public interface IFeedFormatter {
ActionResult Process(FeedContext context, Action populate);
FeedItem AddItem(FeedContext context, ContentItem contentItem);
void AddProperty(FeedContext context, FeedItem feedItem, string name, string value);
}
}

View File

@@ -0,0 +1,12 @@
using Orchard.Core.Feeds.Models;
namespace Orchard.Core.Feeds {
public interface IFeedFormatterProvider : IDependency {
FeedFormatterMatch Match(FeedContext context);
}
public class FeedFormatterMatch {
public int Priority { get; set; }
public IFeedFormatter FeedFormatter { get; set; }
}
}

View File

@@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Orchard.Core.Feeds.Models;
namespace Orchard.Core.Feeds {
public interface IFeedItemBuilder : IEvents {
void Populate(FeedContext context);
}
}

View File

@@ -0,0 +1,7 @@
using Orchard.Core.Feeds.Models;
namespace Orchard.Core.Feeds {
public interface IFeedQuery {
void Execute(FeedContext context);
}
}

View File

@@ -0,0 +1,14 @@
using System.Collections.Generic;
using Orchard.ContentManagement;
using Orchard.Core.Feeds.Models;
namespace Orchard.Core.Feeds {
public interface IFeedQueryProvider : IEvents {
FeedQueryMatch Match(FeedContext context);
}
public class FeedQueryMatch {
public int Priority { get; set; }
public IFeedQuery FeedQuery { get; set; }
}
}

View File

@@ -0,0 +1,22 @@
using System.Collections.Generic;
using System.Web.Mvc;
namespace Orchard.Core.Feeds.Models {
public class FeedContext {
public FeedContext(IValueProvider valueProvider, string format) {
ValueProvider = valueProvider;
Format = format;
Response = new FeedResponse();
FeedData = new Dictionary<string, object>();
}
public IValueProvider ValueProvider { get; set; }
public string Format { get; set; }
public IFeedFormatter FeedFormatter { get; set; }
public IDictionary<string, object> FeedData { get; set; }
public FeedResponse Response { get; set; }
}
}

View File

@@ -0,0 +1,9 @@
using System.Xml.Linq;
using Orchard.ContentManagement;
namespace Orchard.Core.Feeds.Models {
public class FeedItem {
public ContentItem ContentItem { get; set; }
public XElement Element { get; set; }
}
}

View File

@@ -0,0 +1,15 @@
using System.Collections.Generic;
using System.Web.Mvc;
using System.Xml.Linq;
namespace Orchard.Core.Feeds.Models {
public class FeedResponse {
public FeedResponse() {
Items = new List<FeedItem>();
}
public IList<FeedItem> Items { get; set; }
public XElement Element { get; set; }
public ActionResult Result { get; set; }
}
}

View File

@@ -0,0 +1 @@
name: Feeds

View File

@@ -0,0 +1,34 @@
using System.Collections.Generic;
using System.Web.Mvc;
using System.Web.Routing;
using Orchard.Mvc.Routes;
namespace Orchard.Core.Feeds.Rss {
public class Routes : IRouteProvider {
public IEnumerable<RouteDescriptor> GetRoutes() {
return new[] {
new RouteDescriptor {Priority =-10,
Route = new Route(
"rss",
new RouteValueDictionary {
{"area", "Feeds"},
{"controller", "Feed"},
{"action", "Index"},
{"format", "rss"},
},
new RouteValueDictionary(),
new RouteValueDictionary {
{"area", "Feeds"}
},
new MvcRouteHandler())
}
};
}
public void GetRoutes(ICollection<RouteDescriptor> routes) {
foreach (RouteDescriptor routeDescriptor in GetRoutes()) {
routes.Add(routeDescriptor);
}
}
}
}

View File

@@ -0,0 +1,55 @@
using System;
using System.Collections.Generic;
using System.Web.Mvc;
using System.Xml.Linq;
using JetBrains.Annotations;
using Orchard.ContentManagement;
using Orchard.Core.Feeds.Models;
namespace Orchard.Core.Feeds.Rss {
[UsedImplicitly]
public class RssFeedFormatProvider : IFeedFormatterProvider, IFeedFormatter {
public FeedFormatterMatch Match(FeedContext context) {
if (context.Format == "rss") {
return new FeedFormatterMatch {
FeedFormatter = this,
Priority = -5
};
}
return null;
}
public ActionResult Process(FeedContext context, Action populate) {
var rss = new XElement("rss");
rss.SetAttributeValue("version", "2.0");
var channel = new XElement("channel");
context.Response.Element = channel;
rss.Add(channel);
populate();
return new RssResult(new XDocument(rss));
}
public FeedItem AddItem(FeedContext context, ContentItem contentItem) {
var feedItem = new FeedItem {
ContentItem = contentItem,
Element = new XElement("item"),
};
context.Response.Items.Add(feedItem);
context.Response.Element.Add(feedItem.Element);
return feedItem;
}
public void AddProperty(FeedContext context, FeedItem feedItem, string name, string value) {
if (feedItem == null) {
context.Response.Element.Add(new XElement(name, value));
}
else {
feedItem.Element.Add(new XElement(name, value));
}
}
}
}

View File

@@ -0,0 +1,23 @@
using System.Web.Mvc;
using System.Xml;
using System.Xml.Linq;
namespace Orchard.Core.Feeds.Rss {
public class RssResult : ActionResult {
public XDocument Document { get; private set; }
public RssResult(XDocument document) {
Document = document;
}
public override void ExecuteResult(ControllerContext context) {
// not returning application/rss+xml because of
// https://bugzilla.mozilla.org/show_bug.cgi?id=256379
context.HttpContext.Response.ContentType = "text/xml";
using (var writer = XmlWriter.Create(context.HttpContext.Response.Output))
Document.WriteTo(writer);
}
}
}

View File

@@ -0,0 +1,63 @@
using System;
using System.Xml.Linq;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Aspects;
using Orchard.Core.Common.Models;
using Orchard.Core.Feeds.Models;
namespace Orchard.Core.Feeds.Services {
public class CorePartsFeedItemBuilder : IFeedItemBuilder {
private readonly IContentManager _contentManager;
public CorePartsFeedItemBuilder(IContentManager contentManager) {
_contentManager = contentManager;
}
public void Populate(FeedContext context) {
foreach (var feedItem in context.Response.Items) {
// locate parts
var contentItem = feedItem.ContentItem;
var metadata = _contentManager.GetItemMetadata(contentItem);
var common = contentItem.Get<ICommonAspect>();
var routable = contentItem.Get<RoutableAspect>();
var body = contentItem.Get<BodyAspect>();
// standard fields
var link = "/todo";// metadata.DisplayRouteValues();
var title = metadata.DisplayText;
if (string.IsNullOrEmpty(title) && routable != null)
title = routable.Title;
var contentText = title;
if (body != null && !string.IsNullOrEmpty(body.Text))
contentText = body.Text;
DateTime? publishedDate = null;
if (common != null)
publishedDate = common.PublishedUtc ?? common.ModifiedUtc;
// TODO: author
// add to known formats
if (context.Format == "rss") {
feedItem.Element.SetElementValue("title", title);
feedItem.Element.SetElementValue("link", link);
feedItem.Element.SetElementValue("description", contentText);
if (publishedDate != null)
feedItem.Element.SetElementValue("pubDate", publishedDate);//TODO: format
//feedItem.Data.SetElementValue("description", contentText);
feedItem.Element.Add(new XElement("guid", new XAttribute("isPermaLink", "true"), new XText(link)));
}
else {
context.FeedFormatter.AddProperty(context, feedItem, "link", link);
context.FeedFormatter.AddProperty(context, feedItem, "title", title);
context.FeedFormatter.AddProperty(context, feedItem, "description", contentText);
if (publishedDate != null)
context.FeedFormatter.AddProperty(context, feedItem, "published-date", Convert.ToString(publishedDate)); // format? cvt to generic T?
}
}
}
}
}

View File

@@ -0,0 +1,60 @@
using JetBrains.Annotations;
using Orchard.ContentManagement;
using Orchard.Core.Common.Models;
using Orchard.Core.Common.Records;
using Orchard.Core.Feeds.Models;
namespace Orchard.Core.Feeds.StandardQueries {
[UsedImplicitly]
public class ContainerFeedQuery : IFeedQueryProvider, IFeedQuery {
private readonly IContentManager _contentManager;
public ContainerFeedQuery(IContentManager contentManager) {
_contentManager = contentManager;
}
public FeedQueryMatch Match(FeedContext context) {
var containerIdValue = context.ValueProvider.GetValue("containerid");
if (containerIdValue == null)
return null;
return new FeedQueryMatch { FeedQuery = this, Priority = -5 };
}
public void Execute(FeedContext context) {
var containerIdValue = context.ValueProvider.GetValue("containerid");
if (containerIdValue == null)
return;
var limitValue = context.ValueProvider.GetValue("limit");
var limit = 20;
if (limitValue != null)
limit = (int)limitValue.ConvertTo(typeof(int));
var containerId = (int)containerIdValue.ConvertTo(typeof(int));
var container = _contentManager.Get(containerId);
var containerRoutable = container.As<RoutableAspect>();
var containerBody = container.As<BodyAspect>();
if (containerRoutable != null) {
context.FeedFormatter.AddProperty(context, null, "title", containerRoutable.Title);
context.FeedFormatter.AddProperty(context, null, "link", "/" + containerRoutable.Slug);
}
if (containerBody != null) {
context.FeedFormatter.AddProperty(context, null, "description", containerBody.Text);
}
else if (containerRoutable != null) {
context.FeedFormatter.AddProperty(context, null, "description", containerRoutable.Title);
}
var items = _contentManager.Query()
.Where<CommonRecord>(x => x.Container == container.Record)
.OrderByDescending(x => x.PublishedUtc)
.Slice(0, limit);
foreach (var item in items) {
context.FeedFormatter.AddItem(context, item);
}
}
}
}

View File

@@ -81,6 +81,20 @@
<Compile Include="Common\ViewModels\BodyEditorViewModel.cs" />
<Compile Include="Common\ViewModels\RoutableEditorViewModel.cs" />
<Compile Include="Common\ViewModels\OwnerEditorViewModel.cs" />
<Compile Include="Feeds\Controllers\FeedController.cs" />
<Compile Include="Feeds\Routes.cs" />
<Compile Include="Feeds\StandardQueries\ContainerFeedQuery.cs" />
<Compile Include="Feeds\StandardBuilders\CorePartsFeedItemBuilder.cs" />
<Compile Include="Feeds\IFeedFormatter.cs" />
<Compile Include="Feeds\IFeedFormatterProvider.cs" />
<Compile Include="Feeds\IFeedQuery.cs" />
<Compile Include="Feeds\IFeedQueryProvider.cs" />
<Compile Include="Feeds\IFeedItemBuilder.cs" />
<Compile Include="Feeds\Models\FeedContext.cs" />
<Compile Include="Feeds\Models\FeedItem.cs" />
<Compile Include="Feeds\Models\FeedResponse.cs" />
<Compile Include="Feeds\Rss\RssFeedFormat.cs" />
<Compile Include="Feeds\Rss\RssResult.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Scheduling\Records\ScheduledTaskRecord.cs" />
<Compile Include="Scheduling\Services\PublishingTaskHandler.cs" />
@@ -152,6 +166,7 @@
<Content Include="Common\Views\EditorTemplates\Parts\Common.Routable.ascx" />
<Content Include="Common\Views\EditorTemplates\Parts\Common.Body.ascx" />
<Content Include="Common\Views\EditorTemplates\Parts\Common.Owner.ascx" />
<Content Include="Feeds\Package.txt" />
<Content Include="Scheduling\Package.txt" />
<Content Include="Settings\Views\EditorTemplates\Items\Settings.Site.ascx" />
<Content Include="Themes\Scripts\jquery-1.4.js" />