In todays chapter (notice that it's 2.0.8 , yesterdays should
have been 2.0.7, small mistake by santa's magic elf) we'll be
improving Blog 4 Umbraco to include some spam protection. We'll be
checking the comments against the Akismet spam filtering service. But since
Akismet isn't the only spam filtering service we'll be building a
simple provider model to make it easy to add support for whatever
service you want to use.
Before you can use Akismet you need to have a valid API Key (
You can get a free API key by registering for a user account here).
And we'll also need to store this API Key, since Akismet will be
set as the default on new Blog 4 Umbraco installations we'll just
store this on the blog node. So I'll need to add a new property on
the Blog document type.

After adding the new property I'll be able to set the API Key on
the Blog document.
Now I'm ready to start using Akismet. I'll first start by
setting up a simple provider model. The way these spam filtering
services work is that you feed them all the comment info and they
return whether or not the comment is considered spam.
So I'll define an interface that has a Check method with all the
comment info (useragent, ip, author, author email, author url and
the actual comment).
public interface ISpamChecker
{
String ProviderName {
get; set; }
Boolean Check(int
nodeid,
string UserAgent, string UserIp, string Author,
string AuthorEmail, string AuthorUrl, string Content);
}
Next I'll add an abstract class that implements the new
interface
public abstract class SpamChecker: ISpamChecker
{
#region ISpamChecker
Members
private string
m_providername;
public string
ProviderName
{
get { return m_providername; }
set { m_providername = value; }
}
public virtual bool
Check(
int nodeid, string UserAgent, string UserIp,
string Author, string AuthorEmail, string AuthorUrl, string
Content)
{
return false;
}
#endregion
}
And then I'll add the actual Akismet spam checker class (that
inherits from the abstract class and overrides the check method).
There's allready an open source .net wrapper for the Akistmet
service so I'll make use of that.
public class AkismetSpamChecker: SpamChecker
{
public
AkismetSpamChecker()
{
this.ProviderName = "Akismet";
}
public override Boolean
Check(int nodeid,
string
UserAgent, string UserIp, string Author,
string
AuthorEmail, string AuthorUrl, string Content)
{
umbraco.presentation.nodeFactory.Node blog =
new umbraco.presentation.nodeFactory.Node(nodeid);
while (blog.NodeTypeAlias != "Blog")
{
blog = blog.Parent;
}
if
(blog.GetProperty("akismetAPIKey").Value != string.Empty)
{
Akismet api = new Joel.Net.Akismet(
blog.GetProperty("akismetAPIKey").Value,
"http://" +
HttpContext.Current.Request.ServerVariables["HTTP_HOST"] +
blog.Url,
"Blog4Umbraco2");
if (!api.VerifyKey())
{
umbraco.BusinessLogic.Log.Add(
umbraco.BusinessLogic.LogTypes.Error,
-1,
"Akismet Key could not be verified, please check if you have a
valid Akismet API Key");
return false;
};
AkismetComment comment = new AkismetComment();
comment.UserAgent = UserAgent;
comment.UserIp = UserIp;
comment.CommentType = "comment";
comment.CommentAuthor = Author;
comment.CommentAuthorEmail = AuthorEmail;
comment.CommentAuthorUrl = AuthorUrl;
comment.CommentContent = Content;
return api.CommentCheck(comment);
}
else
{
return false;
}
}
}
So in the check method I'll first see if we have a valid API key
(using the nodefactory to get it of the blog document), if that's
ok I'll return the value of the CommentCheck function.
The final step is to actually call this check method when a new
comment is submitted.
Since we want to make it possible to plug in your own spam
checker I'll add a little config file.
<?xml version="1.0"?>
<SpamChecker assembly="/bin/Umlaut.Umb.Blog"
type="Umlaut.Umb.Blog.Spam.AkismetSpamChecker">
</SpamChecker>
That contains the assembly and the type of the spam checker
class that will be used (default we'll use the Akismet one).
By using some reflection we'll call the Check method on the
correct Type (that's setup in the config file).
bool isspam = false;
try
{
string assemblyFile =
HttpContext.Current.Server.MapPath(
String.Format("{0}/..{1}.dll",
GlobalSettings.Path,
Config.GetProviderAssembly()));
Assembly checkerAssembly = Assembly.LoadFrom(assemblyFile);
SpamChecker checker =
(SpamChecker)Activator.CreateInstance(
checkerAssembly.GetType(Config.GetProviderType()));
isspam = checker.Check(parentId,
post.UserAgent, post.UserHostAddress,
name, email, website, comment);
}
catch
(Exception ex) { }
And we'll update the insert statement from yesterdays chapter to
store the value of isspam instead of always storing false.
That's it, our comments will now by checked by Akismet and if
they are seen as spam they won't appear on the blog (since we only
display the comments that aren't flagged as spam).