While Niels is tinkering with the client part of the skinning, I thought I would go back to web developement basics and do some old-fashion Html and javascript.
The current blog comment form is a bit dated, and lacks some polish. I decided to rewrite it, to first of all get some cleaner html, but also to add the following details:
The plan is to:
For this I created a simple static method with a single parameter, the ID of the blog post. It creates a comment and fills in the different properties: email, name, comment etc.
The way it receives input is through the HttpContext request collection, those who remember Asp 3.0 classic will recognise this approach, so no view-state hell. I've added the entire method below. It contains a couple of comments to guide you. The main points are:
public static int CreateComment(int parentId) { //here we find form values posted to the current page HttpRequest post = HttpContext.Current.Request; string email = post["email"]; string comment = post["comment"]; string name = post["name"]; string website = post["website"]; //if all values are there + valid email.. we start to create the comment if (!string.IsNullOrEmpty(email) && isValidEmail(email) && !string.IsNullOrEmpty(comment) && !string.IsNullOrEmpty(name)) { Document blogpost = new Document(parentId); //if parent is actually a blogpost if(blogpost != null && blogpost.ContentType.Alias == "BlogPost"){ DocumentType dt = DocumentType.GetByAlias("BlogPostComment"); Document d = Document.MakeNew("RE: " + blogpost.Text + " by: " + name, dt, new umbraco.BusinessLogic.User(0), blogpost.Id); d.getProperty("comment").Value = comment; d.getProperty("email").Value = email; d.getProperty("name").Value = name; d.getProperty("website").Value = website; d.Save(); d.Publish(new umbraco.BusinessLogic.User(0)); umbraco.library.UpdateDocumentCache(d.Id); return d.Id; } } //if nothing gets created, we return zero return 0; }
public static int CreateComment(int parentId) {
//here we find form values posted to the current page HttpRequest post = HttpContext.Current.Request; string email = post["email"]; string comment = post["comment"]; string name = post["name"]; string website = post["website"];
//if all values are there + valid email.. we start to create the comment if (!string.IsNullOrEmpty(email) && isValidEmail(email) && !string.IsNullOrEmpty(comment) && !string.IsNullOrEmpty(name)) {
Document blogpost = new Document(parentId);
//if parent is actually a blogpost if(blogpost != null && blogpost.ContentType.Alias == "BlogPost"){ DocumentType dt = DocumentType.GetByAlias("BlogPostComment"); Document d = Document.MakeNew("RE: " + blogpost.Text + " by: " + name, dt, new umbraco.BusinessLogic.User(0), blogpost.Id); d.getProperty("comment").Value = comment; d.getProperty("email").Value = email; d.getProperty("name").Value = name; d.getProperty("website").Value = website; d.Save(); d.Publish(new umbraco.BusinessLogic.User(0));
umbraco.library.UpdateDocumentCache(d.Id); return d.Id; } }
//if nothing gets created, we return zero return 0; }
Now that we have some code that can create the comment, we want to make it accessible directly from the browser. For that we use the built in /base, which in short works as an automatic proxy between server-side code and the browser by creating urls that can execute this server side code.
The connection happens in the file /config/restExtensions.config
In here we map the dll Umlaut.Umb.Blog.dll in a <ext> element, add a nice alias, and then add the method CreateComment to the manifest as well:
<ext assembly="/bin/Umlaut.Umb.Blog" type="Umlaut.Umb.Blog.Library.Base" alias="Blog4Umbraco"> <permission method="CreateComment" returnXml="false" allowAll="true" /> </ext>
this means that we can now invoke the method by going to the url /base/Blog4Umbraco/CreateComment/[pageId].aspx we will use this later when jquery will post the form.
The great about doing the form without any asp.net controls is that you rediscover how simple and great html actually is. Install the blog package from: http://nightly.umbraco.org/Blog4Umbraco/2.0.3/ and view the /usercontrols/ajaxcommentform.ascx to see the full markup.
The below sample is the markup for a single field:
<p class="commentField emailField"> <label for="tb_email" class="fieldLabel"> <%= Umlaut.Umb.Blog.BlogLibrary.Dictionary("#Email","Email address") %>: </label> <input type="text" id="tb_email" name="email" class="input-text required email" /> </p>
Things to notice: We have dictionary support for labels (first parameter is the key, and the second is the value to return if the key doesn't exist), and we use jquery.validation for validating the form on the client. We do this by adding the classes "required" and "email" to this field. Which ensures that the field is filled out and is a correct email.
Finally, the core functionality! When the form has been filled out we want to post it with ajax to the /base url, which will handle the comment creation. We do this with jQuery.Post
var url = "/base/Blog4Umbraco/CreateComment/<umbraco:Item field="pageID" runat="server"/>.aspx"; jQuery.post(url, { name: jQuery("#tb_name").val(), email: jQuery("#tb_email").val(), website: jQuery("#tb_website").val(), comment: jQuery("#ta_comment").val() }, function(data){ jQuery("#commentPosted").show(); } });
var url = "/base/Blog4Umbraco/CreateComment/<umbraco:Item field="pageID" runat="server"/>.aspx";
jQuery.post(url, { name: jQuery("#tb_name").val(), email: jQuery("#tb_email").val(), website: jQuery("#tb_website").val(), comment: jQuery("#ta_comment").val() }, function(data){ jQuery("#commentPosted").show(); } });
First we build a url variable. As you ca see it points to /base/Blog4Umbraco/CreateComment/ and we then insert the current page Id on the server side. jQuery then posts a collection of values (email,name,etc) to the /base url. and displays some feedback.
Presto, we have an ajax form.
While we are at it, we'll also add a gravatar preview, so when you enter your email, you will see your gravatar image to the right:
This is also done with jquery and /base:
jQuery("#tb_email").blur(function(){ var email = jQuery("#tb_email").val(); if(email != ""){ var url = "/base/Blog4Umbraco/GetGravatarImage/" + email + "/80.aspx"; jQuery.get(url, function(data){ if(data != ""){ jQuery("#gravatar").css( "background-image","url(" + data + ")" ).show(); }else{ jQuery("#gravatar").hide(); } }); } });
Again, we send simple request to a /base method, this time with 2 parameters, email and image size. The call returns a url to the gravatar image associated with the email.
The great thing about having the form as an ajax form is that automated comment spam doesn't get through. However, neither does people using screenreaders. To ensure that screenreaders can use the form, we need to ensure that it works with javascript turned off. Luckily, we already have the serverside code that expects a collection of values in the Request collection. And when javascript is turned off, the form will post back to it's own url, due to the standard asp.net form. This means that we can execute the /base serverside logic on the page load event in the AjaxCommentForm.ascx like this:
protected void Page_Load(object sender, EventArgs e) { if (Page.IsPostBack) { Library.Base.CreateComment(umbraco.presentation.nodeFactory.Node.GetCurrent().Id); } }
Notice it is the exact same method, it uses the request collection so it works no matter if its executed when a form is post to a /base url, or if the form is post to the page with the macro on.
There has just been posted 2 videos to umbraco.tv about umbraco /base and a video walkthrough of the code explained above. So to get even more info on how to do ajax and umbraco, go to:
http://umbraco.tv/documentation/videos/for-developers/base-and-ajax-development
Also, the code is on codeplex: http://blog4umbraco.codeplex.com/ and as a nightly build: http://nightly.umbraco.org/Blog4Umbraco/2.0.3/
Great work Per. As I was thinking about doing this for my own blog a little while ago. http://screenr.com/hj7 But then I got busy I never fully implmented it into my blog. Thsi blog package is gonna kick ass, will be using this for my new site. It will save me alot of time. Warren :)
Ace! It's really starting to come together! Good work so far. Like the Gravatar previews - along with all the other Ajax stuff!
Hi Per, All sounds great, I look forward to seeing the finally results of the team's hard work :) Just wondered why for the Gravitar you need to post back to your own site to then retrieve the URL for the image, which I assume actually comes from Gravitar? I assume it's something to do with security or a client ID that needs to be passed to Gravitar? Cheers, Chris
@chris there is no reason other then javascript by default does not have a md5 hashing method, so I send it back to the server to hash the email. You can do it with pure javascript, but need a big chunk of code to do it.
This is awesome... Videos are brilliant! Thanks
Hi, nice tutorial. Will be great if there is some check for double post of comment, otherwise F5 or doubleclick on submit button can cause doublepost
I got some problems with the gravatar preview. At first I want to change to default image to something else, how can I do this? Where can I see what the "/base/Blog4Umbraco/GetGravatarImage/" method does? What parameters can it take? The second think is, that I can't get a preview image of any existing gravatar image, I can only see the default "G" image when I type in a fantasy email adress. But if I add my real adress, which is registered with gravatar I can't see my image and I'm not able to post my comment. I'm only getting: "Your comment could not be posted, we're very sorry for the inconvenience"
I got big problems to finde the cs file in umbraco, that solve the problem with gravatar.
Cabal Online The first and the most important task is to get the Cabal Online hacks, As soon as you play this game, you will spend time thinking deeply Cabal Online l guild, how to get the Cabal Online quest, quickly and easily Cabal Online forums, Then sell it to get the Cabal Online cheats.
Is there a way to add captcha or more specifically the recaptcha form to the blog comments area now? i looked up how to do it on the old blog form comments but with the ajax one here now it looks like their tutorials won't work and i cant revert back to the old form either?
Hi, nice tutorial. Will be great if there is some check for double post of comment
Hello! How can I change the label's language?
dsfddf
You comment is being submitted... please wait...
Your comment has been posted on umbraco in reply to: Blog 4 umbraco 2.0.3 – Ajax Comment Form
Something horrible happened. and your comment could not be posted. Please ensure that all fields have correct values, and that yourEmail address is valid.