Widgets are little pieces of user interface that are 100% managed in the browser. You can easily drag them onto your site, rearrange them, removed, and even set properties on them all with your browser. You can use widgets to leverage outside data, call a custom web service, re-use an RSS feed, or even utilize the Graffiti data APIs.
Click here for a list of Graffiti pre-built widgets.
By default, Widgets render with a column-wide UnOrdered List. You can change this within the theme using the macro methods LeftSideBar and RightSideBar.
With-in each individual LI element, an individual widget is rendered. By default a widget's Title is render as an H3 element and the Widget "data" is rendered by overriding the RenderData method.
You can use a number of methods to customize your own widgets and override pre-built Widgets, including:
Widgets are stored in Graffiti's ObjectStore using XML Serialization. If you need to store additional data, you can simply add fields and properties to your derived class and the data is persisted to the database when any changes are made.
Widgets leverage the Graffiti dynamic form API by deriving from Graffiti.Core.EditableForms. You can also leverage this same API to collect additional data from users within your Widget.
Graffiti has a built in API you can use to read external feeds. To make this really easy to do, instead of deriving from Graffiti.Core.Widget, you should derive from Graffiti.Core.WidgetFeed.
The following are examples of pre-built Widgets.
The first example, Title & Body, allows users to quickly and easily add a block of content to their site. The title is a regular textbox and the body is managed with a WYWIWYG editor.
Before we look at the code, here is what's going on:
Here is a look at the code:
/// <summary>/// A widget which displays a title and the a block of text/// </summary>[WidgetInfo("e7abe913-0de7-45a5-9cc5-6c6c5809e808","Title and Body","Represents a title and body")]public class TitleAndBody : Widget{
/// <summary>/// Stores the Content. Title is part of the Widget./// </summary>public string HTML;/// <summary>/// Override name to give a more meaningful/// value when displayed in a list./// </summary>public override string Name{get{if (string.IsNullOrEmpty(Title))return "An empty box";elsereturn Title + " (Title and Body widget)";}}
/// <summary>/// Override DataAsNameValueCollection in include/// the HTML content./// </summary>/// <returns></returns>protected override NameValueCollection DataAsNameValueCollection(){NameValueCollection nvc = base.DataAsNameValueCollection();nvc["HTML"] = HTML;return nvc;}/// <summary>/// Override SetValues so we can pull HTML out of Form object/// and store it locally to be saved to the database./// </summary>public override StatusType SetValues(HttpContext context, NameValueCollection nvc){base.SetValues(context, nvc);HTML = nvc["HTML"];return StatusType.Success;}/// <summary>/// Add our Title and HTML textboxes./// </summary>/// <returns></returns>protected override FormElementCollection AddFormElements(){FormElementCollection fec = new FormElementCollection();fec.Add(new TextFormElement("Title", "Title", "The title of the section"));fec.Add(new WYWIWYGFormElement("HTML", "Your Content", null));return fec;}/// <summary>/// No processing, just render the data./// </summary>/// <returns></returns>public override string RenderData(){return HTML;}}
The second example is from Graffiti's RecentComment widget. This widget allows users to include the last N published comments in the sidebar.
The steps for this are very similar to those in Example One with one major difference. In this Widget, we need to do a bit more work in RenderData to format our comments.
[WidgetInfo("4eaf767d-c787-4e9c-aafc-e37d0ef3f70c","Recent Comments","Controls the display of a list of recent comments")][Serializable]public class RecentCommentsWidget : Widget{public override string Name{get{if (string.IsNullOrEmpty(Title))return "Recent Comments";elsereturn Title;}}public override string Title{get{if (string.IsNullOrEmpty(base.Title))base.Title = "Recent Comments";return base.Title;}set{base.Title = value;}}private int _categoryId = -1;public int CategoryId{get { return _categoryId; }set { _categoryId = value; }}protected override FormElementCollection AddFormElements(){FormElementCollection fec = new FormElementCollection();fec.Add(AddTitleElement());ListFormElement lfe = new ListFormElement("numberOFcomments","Number of Comments","The number of most recent comments to list");lfe.Add(new ListItemFormElement("3", "3"));lfe.Add(new ListItemFormElement("5","5",true));lfe.Add(new ListItemFormElement("10","10"));fec.Add(lfe);return fec;}public override string RenderData(){StringBuilder sb = new StringBuilder("<ul>");sb.Append(new Macros().ULRecentComments(NumberOfComments));sb.Append("</ul>\n");return sb.ToString();}protected override NameValueCollection DataAsNameValueCollection(){NameValueCollection nvc = base.DataAsNameValueCollection();nvc["numberOFcomments"] = NumberOfComments.ToString();nvc["categoryid"] = CategoryId.ToString();return nvc;}public override StatusType SetValues(System.Web.HttpContext context, NameValueCollection nvc){base.SetValues(context, nvc);NumberOfComments = Int32.Parse(nvc["numberOFcomments"]);CategoryId = Int32.Parse(nvc["categoryid"]);return StatusType.Success;}public override string FormName{get{return "Recent Comment Configuration";}}public int NumberOfComments = 5;
In this example we look at the Graffiti Twitter widget. Twitter is a very popular micro-blogging application with which users create 140-character status updates called Tweets. Twitter has a couple of API endpoints, but here we will leverage a very simple RSS feed which contains our recent tweets.
Before looking through the code, there is one very important thing to call out. We are deriving from the WidgeFeed instead of Widget. This gives us access to the Graffiti FeedManager which will not only request any RSS, Atom, or Opml feed for us, but it will also own the responsibility of caching and storing this data every hour. This way our site and pages are always very responsive, even when the service for our data may not be.
[WidgetInfo("3ec475ab-cd5c-47f6-8e37-e7752a46cc5a","Twitter","Twitter messages")]public class TwitterWidget : WidgetFeed{private string _UserName;public string UserName{get { return _UserName; }set { _UserName = value; }}private int _itemsToDisplay = 3;public int ItemsToDisplay{get { return _itemsToDisplay; }set { _itemsToDisplay = value; }}public override string FeedUrl{get { return "http://twitter.com/statuses/user_timeline/" + UserName + ".rss"; }}public override string RenderData(){StringBuilder sb = new StringBuilder("<ul>");if (!string.IsNullOrEmpty(UserName)){try{RssChannel channel = this.Document();if (channel != null && channel.Items != null){int min = Math.Min(channel.Items.Count, ItemsToDisplay);for (int i = 0; i < min; i++){string desc = channel.Items[i].Description;int index = desc.IndexOf(":");sb.Append("<li>" + ( (index > -1) ? desc.Substring(index + 1).Trim() : desc) + "</li>");}}}catch(Exception){}sb.Append("</ul>\n");}return sb.ToString();}public override string Title{get{if (string.IsNullOrEmpty(base.Title))base.Title = "My Tweets";return base.Title;}set{base.Title = value;}}public override string Name{get{return "Twitter";}}protected override FormElementCollection AddFormElements(){FormElementCollection fec = new FormElementCollection();fec.Add(AddTitleElement());fec.Add(new TextFormElement("username", "UserName", "(your twitter username"));ListFormElement lfe = new ListFormElement("itemsToDisplay","Number of Tweets","(how many tweets do you want to display?)");lfe.Add(new ListItemFormElement("1","1"));lfe.Add(new ListItemFormElement("3", "3", true));lfe.Add(new ListItemFormElement("5", "5"));fec.Add(lfe);return fec;}protected override NameValueCollection DataAsNameValueCollection(){NameValueCollection nvc = base.DataAsNameValueCollection();nvc["username"] = UserName;nvc["itemsToDisplay"] = ItemsToDisplay.ToString();return nvc;}public override StatusType SetValues(HttpContext context, NameValueCollection nvc){StatusType statusType = base.SetValues(context, nvc);if(statusType == StatusType.Success){ItemsToDisplay = Int32.Parse(nvc["itemsToDisplay"]);UserName = nvc["username"];try{RegisterForSyndication();}catch(Exception ex){statusType = StatusType.Error;SetMessage(context,ex.Message);}}return statusType;}}