Geeks With Blogs
Doug Butscher Nothin' but .NET!

graffiti-logo-270907_3 We're using Graffiti CMS as our CMS for Coders 4 Charities, a site that we built to spread the word about our charity event coming late April 2008. We needed to implement a registration page, so that developers and charities can register for the event. Graffiti is obviously very powerful and easy to use, but I had a hard time figuring out how to get user entry to work.

Here is my fundamental understanding of how things kinda work:

When you create a post, Graffiti will create a folder for your post, and create a default.aspx page. If you categorize the post, it will put your new page under the category sub-dir. When your default.aspx page renders, Graffiti will look for a .view file that matches the name of your post (for example, if your post name is Registration, the path will be [site root]/registration/default.aspx.) If Graffiti cannot find a registration.view file,it looks for default index.view and layout.view files, which tell it how to render the themes. If it finds your registration.view file, it will render according to the template in that view.

Graffiti has nice Contact and Comment out-of-the-box functionality, and it uses macros to code-gen the javascript in the view files. After some digging, I found out that the javascript function call is performing an Ajax.Request to a HTTP handler, which is compiled into the Graffiti.Core library. The button click event passes the path to the .ashx file, and the HTML <form> is serialized as the parameter collection passed to the handler. Somewhere in that core library the magic happens, the Request is processed, the contact, comment or your operation is processed, and the handler does a Response.Write to send back results.

So, these are the steps I took to implement a registration page on Coders 4 Charities (which, at this time, is not yet fully implemented)

1) Create your post. For this blog, we'll call it Registration. I put it in a category called registration. Put something basic in the body, it will be thrown out by your view file. UPDATED: 2/6/08: I was wrong, it will not be thrown out by the view file. It creates an actual post that is navigable if you put it in a category. I added it to the uncategorized category and checked all of the options that pretty much tell the post to not display.

2) Create the javascript file that will handle your button onClick event. I called mine c4c.js and I put it in the __utility\js folder (where the graffiti.js and prototype.js files are. NOTE: you need the protoype.js file to handle your Ajax.Request call!!).

function C4CRegisterDeveloper(url)
{
    new Ajax.Request(url,
    {
        method:'post',
        parameters:  Form.serialize('register_form'),
        onSuccess: function(transport)
                    {
                         var response = transport.responseText || "no response text";
                         alert(response);
                    },
        onFailure: function()
        {
            alert("Something went wrong");
        }
  });

}

As you can see, this function (for now) simply throws the response from the Ajax.Request call into an alert() call, so I can make sure we made it round trip.

3) Create a new .view file with the name of your post (registration.view). Mine has a bunch of HTML <table>-based labels and text-boxes, followed by the submit button. I opted to not use the Graffiti macros for my submit button, because I didn't want to extend their macro library. Hard-coding works fine for me in this case. In addition, this view file contains the reference to my c4c.js file. Here is the markup for the registration.view file:

<script type="text/javascript" src="/__utility/js/c4c.js"></script>

<form action="$url" method="post" id="register_form">
<table>
<tr>
<td> <label class="form_field_pair" for="firstlast">Name</label> </td>
<td>
<input class="form_field" type="text" name="_firstname" id="_firstname" value="" tabindex="1" />
<input class="form_field" type="text" name="_lastname" id="_lastname" value="" tabindex="2" />
</td>
</tr>

<tr>
<td> <label class="form_field_pair" for="address">Address</label> </td>
<td> <input class="form_field" type="text" name="_addresslineone" id="_addresslineone" value="" tabindex="3" /> </td>
</tr>

<tr>
<td> &nbsp;</td>
<td>
<input class="form_field" type="text" name="_addresslinetwo" id="_addresslinetwo" value="" tabindex="4" />
</td>
</tr>

<tr>
<td> <label class="form_field_pair" for="citystatezip">City, State, Zip</label> </td>
<td> <input class="form_field" type="text" name="_city" id="_city" value="" tabindex="5" />
<select class="form_field" name="_state" id="_state" tabindex="6">
    <option selected value="--">--</option>
...
</select>
<input class="form_field" type="text" name="_zipcode" id="_zipcode" value="" tabindex="7" />
</td>
</tr>

<tr>
<td> <label class="form_field_pair" for="email">Email</label> </td>
<td> <input class="form_field" type="text" name="_email" id="_email" value="" tabindex="8" /> </td>
</tr>

<tr>
<td> <label class="form_field_pair" for="phone">Phone</label> </td>
<td> <input class="form_field" type="text" name="_phonenumber" id="_phonenumber" value="" tabindex="9" /> </td>
</tr>

<tr>
<td> <label class="form_field_pair" for="phone">Cell</label> </td>
<td> <input class="form_field" type="text" name="_phonenumbertwo" id="_phonenumbertwo" value="" tabindex="10" /> </td>
</tr>

<tr>
<td> <label class="form_field_pair" for="talents">Talents</label> </td>
<td> <textarea class="form_field" id="_talents" name="_talents" cols="20" rows="5" tabindex="11"></textarea> </td>
</tr>

<tr>
<td> &nbsp; </td>
<td> <input type="button" name="Register" value="Register" id="Register" onClick="C4CRegisterDeveloper('/c4c.ashx');" /> </td>
</tr>

</table>
<div style="display:none;" id="contact_status"></div>
</form>

This can obviously be cleaned up, but hey, I'm still prototyping.

 

4) Create your Generic HTTP Handler. I created a C# Web Project, added a Generic Handler template, and named it c4c.ashx. VS will create the code-behind for you, and the end result looks like this (with my code added):

using System;
using System.Data;
using System.Web;
using System.Collections;
using System.Web.Services;
using System.Web.Services.Protocols;

namespace C4CWeb
{
    /// <summary>
    /// Summary description for $codebehindclassname$
    /// </summary>
    [WebService(Namespace = "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    public class c4c : IHttpHandler
    {

        public void ProcessRequest(HttpContext context)
        {
            context.Response.ContentType = "text/plain";
            System.Text.StringBuilder myResponse = new System.Text.StringBuilder();
            myResponse.Append(context.Request["_firstname"]);
            myResponse.Append(" ");
            myResponse.Append(context.Request["_lastname"]);
            myResponse.Append("\r\n");
            myResponse.Append(context.Request["_addresslineone"]);
            myResponse.Append("\r\n");
            myResponse.Append(context.Request["_addresslinetwo"]);
            myResponse.Append("\r\n");
            myResponse.Append(context.Request["_city"]);
            myResponse.Append(", ");
            myResponse.Append(context.Request["_state"]);
            myResponse.Append(" ");
            myResponse.Append(context.Request["_zipcode"]);
            myResponse.Append("\r\n");
            myResponse.Append(context.Request["_email"]);
            myResponse.Append("\r\n");
            myResponse.Append(context.Request["_phonenumber"]);
            myResponse.Append("\r\n");
            myResponse.Append(context.Request["_phonenumbertwo"]);
            myResponse.Append("\r\n");
            myResponse.Append(context.Request["_talents"]);
            context.Response.Write(myResponse);
        }

        public bool IsReusable
        {
            get
            {
                return false;
            }
        }
    }
}

For now, all I am doing is making sure that I can parse off the elements in the serialized <form>. This is just proof-of-concept, and not clean by any means. I just want to pull the parameters off the context.Request object, assemble a string, and send it back to the caller to confirm the round trip. This is where you add your business logic. I still need to take these parameters and store them in my data source.

 

5) Push it up. I put the c4c.ashx file off the site root, and my C4CWeb.dll in the bin folder in Graffiti.

 

I think that should be everything! If I have forgotten anything, or if you have any questions, post a comment to this blog and I'll try to help.

Doug

Posted on Tuesday, January 29, 2008 8:55 AM Coders For Charities | Back to top


Comments on this post: Graffiti CMS | User Entry

# re: Graffiti CMS | User Entry
Requesting Gravatar...
Hello,

I just wanted to thank you for such a great post. I was pulling my hair trying to figure out how to create a similar form like this one and writing some code behind. I really appreciate it :)
Left by Claudia on Jun 06, 2009 9:18 PM

# re: Graffiti CMS | User Entry
Requesting Gravatar...
Thank you. Thank you. Thank you. I spent nearly two hours trying to get some help on working with Graffiti to do custom interaction and your post helped me TREMENDOUSLY!!!
Left by Amir on Jun 21, 2009 11:13 AM

# re: Graffiti CMS | User Entry
Requesting Gravatar...
More and more people show somewhat skills in thinking and even more tend to assure originality. Even free papers is sharp and exact.
Left by Nardavind on Jan 24, 2013 6:43 AM

Your comment:
 (will show your gravatar)


Copyright © Doug Butscher | Powered by: GeeksWithBlogs.net