#region Copyright (c) 2003, newtelligence AG. All rights reserved.
/*
// Copyright (c) 2003, newtelligence AG. (http://www.newtelligence.com)
// Original BlogX Source Code: Copyright (c) 2003, Chris Anderson (http://simplegeek.com)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are permitted
// provided that the following conditions are met:
//
// (1) Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
// (2) Redistributions in binary form must reproduce the above copyright notice, this list of
// conditions and the following disclaimer in the documentation and/or other materials
// provided with the distribution.
// (3) Neither the name of the newtelligence AG nor the names of its contributors may be used
// to endorse or promote products derived from this software without specific prior
// written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
// OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
// IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// -------------------------------------------------------------------------
//
// Original BlogX source code (c) 2003 by Chris Anderson (http://simplegeek.com)
//
// newtelligence is a registered trademark of newtelligence Aktiengesellschaft.
//
// For portions of this software, the some additional copyright notices may apply
// which can either be found in the license.txt file included in the source distribution
// or following this notice.
//
*/
#endregion
namespace newtelligence.DasBlog.Web.Services{
using System;
using System.Configuration;
using System.Collections;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Web.Services.Description;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using newtelligence.DasBlog.Runtime;
using newtelligence.DasBlog.Web.Services.Rss20;
using newtelligence.DasBlog.Web.Services.Atom10;
using newtelligence.DasBlog.Web.Core;
using newtelligence.DasBlog.Util.Html;
using System.ServiceModel.Web;
using System.Net;
using System.ServiceModel;
using System.ServiceModel.Syndication;
using System.Collections.Generic;
using System.Web.Security;
/// <summary>
/// Summary description for DasBlogBrowsing.
/// </summary>
[WebService(Namespace="urn:schemas-newtelligence-com:dasblog:syndication-services")]
public partial class WebWebServices
{
protected bool inASMX = (HttpContext.Current!= null);
[OperationContract, WebGet(UriTemplate="/rssurl")]
public string GetRssUrl()
{
return Utils.GetRssUrl(siteConfig);
}
[OperationContract, WebGet(UriTemplate = "/commentsrssurl")]
public string GetCommentsRssUrl()
{
return Utils.GetCommentsRssUrl(siteConfig);
}
[OperationContract, WebGet(UriTemplate = "/comments?format=rss&guid={guid}")]
public Rss20FeedFormatter GetEntryCommentsRss(string guid)
{
CommentCollection _com;
if ( guid == null )
_com = dataService.GetAllComments();
else
_com = dataService.GetPublicCommentsFor(guid);
_com = ReorderComments(_com);
return new Rss20FeedFormatter(GetCommentsFeedCore(_com,guid));
}
[OperationContract, WebGet(UriTemplate = "/comments?format=atom&guid={guid}")]
public Atom10FeedFormatter GetEntryCommentsAtom(string guid)
{
CommentCollection _com;
if (guid == null)
_com = dataService.GetAllComments();
else
_com = dataService.GetPublicCommentsFor(guid);
_com = ReorderComments(_com);
return new Atom10FeedFormatter(GetCommentsFeedCore(_com,guid));
}
[OperationContract, WebGet(UriTemplate = "/feed?format=rss&category={category}&maxDays={maxDayCount}&maxEntries={maxEntryCount}")]
public Rss20FeedFormatter GetRss(string category, string maxDayCount, string maxEntryCount)
{
int maxDays = siteConfig.RssDayCount;
int maxEntries = siteConfig.RssMainEntryCount;
if (maxDayCount != null) maxDays = int.Parse(maxDayCount);
if (maxEntryCount != null) maxEntries = int.Parse(maxEntryCount);
return new Rss20FeedFormatter(GetFeedCore(category, maxDays, maxEntries));
}
[OperationContract, WebGet(UriTemplate = "/feed?format=atom&category={category}&maxDays={maxDayCount}&maxEntries={maxEntryCount}")]
public Atom10FeedFormatter GetAtom(string category, string maxDayCount, string maxEntryCount)
{
int maxDays = siteConfig.RssDayCount;
int maxEntries = siteConfig.RssMainEntryCount;
if (maxDayCount != null) maxDays = int.Parse(maxDayCount);
if (maxEntryCount != null) maxEntries = int.Parse(maxEntryCount);
return new Atom10FeedFormatter(GetFeedCore(category, maxDays, maxEntries));
}
private bool RedirectToFeedBurnerIfNeeded(string category)
{
if( inASMX )
{
//If we are using FeedBurner, only allow them to get our feed...
if(siteConfig.FeedBurnerName != null && siteConfig.FeedBurnerName.Length > 0)
{
HttpContext current = HttpContext.Current;
string userAgent = current.Request.UserAgent;
if(userAgent != null && userAgent.Length >0)
{
// If they aren't FeedBurner and they aren't asking for a category, redirect them!
if (userAgent.StartsWith("FeedBurner") == false && (category == null || category.Length == 0))
{
current.Response.StatusCode = 301;
current.Response.Status = "301 Moved Permanently";
current.Response.RedirectLocation = Utils.GetRssUrl(siteConfig);
return true;
}
}
}
}
return false;
}
private SyndicationFeed GetFeedCore(string category, int maxDayCount, int maxEntryCount)
{
if (RedirectToFeedBurnerIfNeeded(category) == true)
{
return null;
}
EntryCollection entries = null;
//We only build the entries if blogcore doesn't exist and we'll need them later...
if (dataService.GetLastEntryUpdate() == DateTime.MinValue)
{
entries = BuildEntries(category, maxDayCount, maxEntryCount);
}
//Try to get out as soon as possible with as little CPU as possible
if ( inASMX )
{
DateTime lastModified = Utils.GetLatestModifedEntryDateTime(dataService, entries);
if (Utils.GetStatusNotModified(lastModified))
return null;
}
if ( inASMX )
{
string referrer = WebOperationContext.Current.IncomingRequest.Headers[HttpRequestHeader.Referer];
if (ReferralBlackList.IsBlockedReferrer(referrer))
{
if (siteConfig.EnableReferralUrlBlackList404s)
{
return null;
}
}
else
{
loggingService.AddReferral(
new LogDataItem(
WebOperationContext.Current.IncomingRequest.UriTemplateMatch.RequestUri.ToString(),
referrer,
WebOperationContext.Current.IncomingRequest.Headers[HttpRequestHeader.UserAgent],
HttpContext.Current.Request.UserHostName));
}
}
//not-modified didn't work, do we have this in cache?
string CacheKey = "Rss:" + category + ":" + maxDayCount.ToString() + ":" + maxEntryCount.ToString();
SyndicationFeed feed = System.Web.Hosting.HostingEnvironment.Cache[CacheKey] as SyndicationFeed;
if (feed == null) //we'll have to build it...
{
//However, if we made it this far, the not-modified check didn't work, and we may not have entries...
if(entries == null)
{
entries = BuildEntries(category, maxDayCount, maxEntryCount);
}
DateTime lastBuildDate = DateTime.Today;
List<SyndicationItem> items = new List<SyndicationItem>();
foreach (Entry entry in entries)
{
if (entry.IsPublic == false || entry.Syndicated == false)
{
continue;
}
SyndicationItem item = new SyndicationItem();
item.Title = SyndicationContent.CreatePlaintextContent(entry.Title);
item.Id = Utils.GetPermaLinkUrl(siteConfig, entry.EntryId);
item.Links.Add(SyndicationLink.CreateAlternateLink(new Uri(Utils.GetPermaLinkUrl(siteConfig, entry.EntryId))));
MembershipUser user = Membership.GetUser(entry.Author);
//Scott Hanselman: According to the RSS 2.0 spec and FeedValidator.org,
// we can have EITHER an Author tag OR the preferred dc:creator tag, but NOT BOTH.
// if (user != null && user.EmailAddress != null && user.EmailAddress.Length > 0)
// {
// if (user.DisplayName != null && user.DisplayName.Length > 0)
// {
// item.Author = String.Format("{0} ({1})", user.EmailAddress, user.DisplayName);
// }
// else
// {
// item.Author = user.EmailAddress;
// }
// }
XmlDocument doc2 = new XmlDocument();
XmlElement trackbackPing = doc2.CreateElement("trackback", "ping", "http://madskills.com/public/xml/rss/module/trackback/");
trackbackPing.InnerText = Utils.GetTrackbackUrl(siteConfig, entry.EntryId);
item.ElementExtensions.Add(trackbackPing);
XmlElement pingbackServer = doc2.CreateElement("pingback", "server", "http://madskills.com/public/xml/rss/module/pingback/");
pingbackServer.InnerText = new Uri(new Uri(Utils.GetBaseUrl(siteConfig)), "pingback.svc").ToString();
item.ElementExtensions.Add(pingbackServer);
XmlElement pingbackTarget = doc2.CreateElement("pingback", "target", "http://madskills.com/public/xml/rss/module/pingback/");
pingbackTarget.InnerText = Utils.GetPermaLinkUrl(siteConfig, entry.EntryId);
item.ElementExtensions.Add(pingbackTarget);
XmlElement dcCreator = doc2.CreateElement("dc", "creator", "http://purl.org/dc/elements/1.1/");
if (user != null)
{
// HACK AG No author e-mail address in feed items.
// if (user.DisplayName != null && user.DisplayName.Length > 0)
// {
// if(user.EmailAddress != null && user.EmailAddress.Length > 0)
// {
// dcCreator.InnerText = String.Format("{0} ({1})", user.EmailAddress, user.DisplayName);
// }
// else
// {
dcCreator.InnerText = user.UserName;
// }
// }
// else
// {
// dcCreator.InnerText = user.EmailAddress;
// }
}
item.ElementExtensions.Add(dcCreator);
if (siteConfig.EnableComments)
{
if (entry.AllowComments)
{
XmlElement commentApi = doc2.CreateElement("wfw", "comment", "http://wellformedweb.org/CommentAPI/");
commentApi.InnerText = Utils.GetCommentViewUrl(siteConfig, entry.EntryId);
item.ElementExtensions.Add(commentApi);
}
XmlElement commentRss = doc2.CreateElement("wfw", "commentRss", "http://wellformedweb.org/CommentAPI/");
commentRss.InnerText = Utils.GetEntryCommentsRssUrl(siteConfig, entry.EntryId);
item.ElementExtensions.Add(commentRss);
//for RSS conformance per FeedValidator.org
int commentsCount = dataService.GetPublicCommentsFor(entry.EntryId).Count;
if (commentsCount > 0)
{
XmlElement slashComments = doc2.CreateElement("slash", "comments", "http://purl.org/rss/1.0/modules/slash/");
slashComments.InnerText = commentsCount.ToString();
item.ElementExtensions.Add(slashComments);
}
//FIX: item.Comments = Utils.GetCommentViewUrl(siteConfig, entry.EntryId);
}
//FIX: item.Language = entry.Language;
if (entry.Categories != null && entry.Categories.Length > 0)
{
string[] cats = entry.Categories.Split(';');
foreach (string c in cats)
{
item.Categories.Add(new SyndicationCategory(c.Replace('|', '/')));
}
}
foreach( Attachment attachment in entry.Attachments)
{
// RSS currently supports only a single enclsoure so we return the first one
item.Links.Add( SyndicationLink.CreateMediaEnclosureLink(
new Uri(Utils.GetEnclosureLinkUrl(entry.EntryId, attachment)),
attachment.Type,
attachment.Length));
}
item.PublishDate = entry.CreatedUtc;
lastBuildDate = entry.CreatedUtc;
if (!siteConfig.AlwaysIncludeContentInRSS &&
entry.Description != null &&
entry.Description.Trim().Length > 0)
{
item.Content = SyndicationContent.CreateHtmlContent(PreprocessItemContent(entry.EntryId, entry.Description));
}
else
{
item.Content = SyndicationContent.CreateHtmlContent(ContentFormatter.FormatContentAsHTML(PreprocessItemContent(entry.EntryId, entry.Content)));
//try
//{
// doc2.LoadXml(ContentFormatter.FormatContentAsXHTML(PreprocessItemContent(entry.EntryId, entry.Content)));
// anyElements.Add((XmlElement)doc2.SelectSingleNode("//*[local-name() = 'body'][namespace-uri()='http://www.w3.org/1999/xhtml']"));
//}
//catch //(Exception ex)
//{
// //Debug.Write(ex.ToString());
// // absorb
//}
}
items.Add(item);
}
feed = new SyndicationFeed(items);
//feed.Namespaces.Add("dc","http://purl.org/dc/elements/1.1/");
//feed.Namespaces.Add("trackback","http://madskills.com/public/xml/rss/module/trackback/");
//feed.Namespaces.Add("pingback","http://madskills.com/public/xml/rss/module/pingback/");
//if (siteConfig.EnableComments)
//{
// feed.Namespaces.Add("wfw","http://wellformedweb.org/CommentAPI/");
// feed.Namespaces.Add("slash","http://purl.org/rss/1.0/modules/slash/");
//}
//RssChannel ch = new RssChannel();
if(category == null)
{
feed.Title = new TextSyndicationContent(siteConfig.Title);
}
else
{
feed.Title = new TextSyndicationContent(siteConfig.Title + " - " + category);
}
if ( siteConfig.Description == null || siteConfig.Description.Trim().Length == 0 )
{
feed.Description = new TextSyndicationContent(siteConfig.Subtitle);
}
else
{
feed.Description= new TextSyndicationContent(siteConfig.Description);
}
feed.Links.Add(SyndicationLink.CreateAlternateLink(new Uri(Utils.GetBaseUrl(siteConfig))));
feed.Copyright = new TextSyndicationContent(siteConfig.Copyright);
if (siteConfig.RssLanguage != null && siteConfig.RssLanguage.Length >0)
{
feed.Language = siteConfig.RssLanguage;
}
feed.Authors.Add(new SyndicationPerson(siteConfig.Contact));
feed.ImageUrl = null;
if (siteConfig.ChannelImageUrl != null && siteConfig.ChannelImageUrl.Trim().Length > 0)
{
if (siteConfig.ChannelImageUrl.StartsWith("http"))
{
feed.ImageUrl = new Uri(siteConfig.ChannelImageUrl);
}
else
{
feed.ImageUrl = new Uri(Utils.RelativeToRoot(siteConfig,siteConfig.ChannelImageUrl));
}
}
System.Web.Hosting.HostingEnvironment.Cache.Insert(CacheKey,feed,null,DateTime.Now.AddMinutes(5),System.Web.Caching.Cache.NoSlidingExpiration);
}
return feed;
}
private CommentCollection ReorderComments(CommentCollection _comments)
{
CommentCollection tempCol = new CommentCollection( _comments );
tempCol.Sort( new CommentSorter() );
return tempCol;
}
private SyndicationFeed GetCommentsFeedCore(CommentCollection _com)
{
return GetCommentsFeedCore(_com,String.Empty);
}
private SyndicationFeed GetCommentsFeedCore(CommentCollection _com, string guid)
{
//Try to get out as soon as possible with as little CPU as possible
if ( inASMX )
{
DateTime lastModified = Utils.GetLatestModifedCommentDateTime(dataService, _com);
if (Utils.GetStatusNotModified(lastModified))
return null;
}
if ( inASMX )
{
string referrer = WebOperationContext.Current.IncomingRequest.Headers[HttpRequestHeader.Referer];
if (ReferralBlackList.IsBlockedReferrer(referrer) == false)
{
loggingService.AddReferral(
new LogDataItem(
WebOperationContext.Current.IncomingRequest.UriTemplateMatch.RequestUri.ToString(),
referrer,
WebOperationContext.Current.IncomingRequest.Headers[HttpRequestHeader.UserAgent],
HttpContext.Current.Request.UserHostName));
}
}
int i = 0;
List<SyndicationItem> items = new List<SyndicationItem>();
foreach (Comment c in _com)
{
SyndicationItem item = new SyndicationItem();
if (i == siteConfig.RssEntryCount) { break; }
i++;
string tempTitle = "";
if(c.TargetTitle != null && c.TargetTitle.Length > 0 )
{
tempTitle = " on \"" + c.TargetTitle + "\"";
}
if(c.Author == null || c.Author == "")
{
item.Title = new TextSyndicationContent( "Comment" + tempTitle );
}
else
{
item.Title = new TextSyndicationContent("Comment by " + c.Author + tempTitle);
}
//Per the RSS Comments Spec it makes more sense for guid and link to be the same.
// http://blogs.law.harvard.edu/tech/rss#comments
// 11/11/05 - SDH - Now, I'm thinking not so much...
item.Id = Utils.GetCommentViewUrl(siteConfig, c.TargetEntryId, c.EntryId);
item.Links.Add(SyndicationLink.CreateAlternateLink(new Uri(Utils.GetCommentViewUrl(siteConfig, c.TargetEntryId, c.EntryId))));
item.PublishDate = c.CreatedUtc;
item.Content = SyndicationContent.CreateHtmlContent(c.Content.Replace(Environment.NewLine, "<br />"));
if (c.AuthorHomepage == null || c.AuthorHomepage == "")
{
if (c.AuthorEmail == null || c.AuthorEmail == "")
{
if(!(c.Author == null || c.Author == "") )
{
item.Content = SyndicationContent.CreateHtmlContent(c.Content.Replace(Environment.NewLine, "<br />") + "<br /><br />" + "Posted by: " + c.Author);
}
}
else
{
string content = c.Content.Replace(Environment.NewLine, "<br />");
if (!siteConfig.SupressEmailAddressDisplay)
{
item.Content = SyndicationContent.CreateHtmlContent(content + "<br /><br />" + "Posted by: " + "<a href=\"mailto:" + Utils.SpamBlocker(c.AuthorEmail) + "\">" + c.Author + "</a>");
}
else
{
item.Content = SyndicationContent.CreateHtmlContent(content + "<br /><br />" + "Posted by: " + c.Author);
}
}
}
else
{
if (c.AuthorHomepage.IndexOf("http://") < 0)
{
c.AuthorHomepage = "http://" + c.AuthorHomepage;
}
item.Content = SyndicationContent.CreateHtmlContent(c.Content.Replace(Environment.NewLine, "<br />")+"<br /><br />" + "Posted by: " + "<a href=\"" + c.AuthorHomepage +
"\">" + c.Author + "</a>");
}
if (c.Author != null && c.Author.Length > 0 )
{
// the rss spec requires an email address in the author tag
// and it can not be obfuscated
// according to the feedvalidator
string email;
if ( !siteConfig.SupressEmailAddressDisplay )
{
email = (c.AuthorEmail != null && c.AuthorEmail.Length > 0 ? c.AuthorEmail : "unknown@unknown.org");
}
else
{
email = "suppressed@unknown.org";
}
item.Authors.Add(new SyndicationPerson(email, c.Author, c.AuthorHomepage));
}
//FIX: item.Comments = Utils.GetCommentViewUrl(siteConfig,c.TargetEntryId,c.EntryId);
//ch.LastBuildDate = item.PubDate;
items.Add(item);
}
// TODO: Figure out why this code is copied and pasted from above rather than using a function (shame!)
SyndicationFeed feed = new SyndicationFeed(items);
if (guid != null && guid != String.Empty)
{
//Set the title for this RSS Comments feed
feed.Title = new TextSyndicationContent(siteConfig.Title + " - Comments on " + dataService.GetEntry(guid).Title);
}
else
{
feed.Title = new TextSyndicationContent(siteConfig.Title + " - Comments");
}
feed.Links.Add(SyndicationLink.CreateAlternateLink(new Uri(Utils.GetBaseUrl(siteConfig))));
feed.Copyright = new TextSyndicationContent(siteConfig.Copyright);
feed.Authors.Add(new SyndicationPerson(siteConfig.Contact));
return feed;
}
//[OperationContract, WebGet(UriTemplate = "/GetReferrerLog?date={dateString}")]
public LogDataItemCollection GetReferrerLog(string dateString)
{
DateTime date = DateTime.Parse(dateString);
return loggingService.GetReferralsForDay(date);
}
//[OperationContract, WebGet(UriTemplate = "/GetCategoryList")]
public string[] GetCategoryList()
{
CategoryCacheEntryCollection categories = dataService.GetCategories();
string[] cats = new string[categories.Count];
for (int i=0; i<cats.Length; i++)
{
cats[i] = categories[i].Name;
}
return cats;
}
//[OperationContract, WebGet(UriTemplate = "/GetCategoryEntries?category={categoryName}")]
public EntryCollection GetCategoryEntries(string categoryName)
{
return dataService.GetEntriesForCategory( categoryName, null );
}
//[OperationContract, WebGet(UriTemplate = "/GetDaysWithEntries")]
public DateTime[] GetDaysWithEntries()
{
return dataService.GetDaysWithEntries(new newtelligence.DasBlog.Util.UTCTimeZone());
}
//[OperationContract, WebGet(UriTemplate = "/GetDayEntry?date={dateString}")]
public DayEntry GetDayEntry(string dateString)
{
DateTime date = DateTime.Parse(dateString);
return dataService.GetDayEntry(date);
}
//[OperationContract, WebGet(UriTemplate = "/GetDayExtra?date={dateString}")]
public DayExtra GetDayExtra(string dateString)
{
DateTime date = DateTime.Parse(dateString);
return dataService.GetDayExtra(date);
}
}
}
|