Making the Calendar into a Graffiti CMS Widget

Last time I kind of trailed off. The Archive Calendar was working fine as a Chalk extension and I had a business trip to make for a week and doing the work to change it into a widget wasn't at the top of my list.

Earlier this week, though, back safely in Colorado Springs, I spent a couple of hours extracting it all out as a joint widget/Chalk extension assembly, all ready for posting on CodePlex. Since this is the first widget I'd written, and since the official help page is a little, er, brief on the whys and hows, I thought I'd write it all up.

First off, like the official help says, to create a widget you "create a class which derives from Graffiti.Core.Widget and is marked as being Serializable." And then, "Add the WidgetInfo attribute and specify a uniqueid (Guid), a name, and a description."

  [Serializable]
  [WidgetInfo("D6704F1F-AA4B-4d94-838F-9F8E7306F5D9", "JmbCalendar", "Archive post calendar using caching")]
  public class JmbCalendar : Widget {
    //..
  }

The uniqueid parameter of the WidgetInfo attribute constructor merely has to be unique; it doesn't have to be a GUID per se. It's just easier to use a GUID to make sure of uniqueness. (Use Visual Studio's Create GUID option in the Tools menu.) The Serializable part is very important as we'll see later, and it's a shame that a couple of the examples on the help page have this attribute missing. So don't forget it.

Next up: "Implement the abstract methods of Widget (RenderData and Name)." RenderData is the method that will be called to generate the HTML string that will render the widget in the sidebar.

    public override string RenderData() {
      int year;
      int month;
      GetMonth(out year, out month);

      GraffitiCalendar calendar = new GraffitiCalendar(year, month, PrevMonthFormat, NextMonthFormat);
      return calendar.ToString();
    }

For me, since I'd written all the underlying code already it was pretty simple. However, the Chalk extension was able to get the year and month from its call in the view. The widget doesn't have that luxury, so I had to write a method called GetMonth() to get the year and moonth parameters form the query part of the HTTP request.

    private void GetMonth(out int year, out int month) {
      string yearAsString = string.Empty;
      string monthAsString = string.Empty;
      GraffitiContext gc = GraffitiContext.Current;
      HttpRequest request = (HttpRequest)gc.InternalGet("request");
      if (request != null) {
        yearAsString = request.QueryString["year"];
        monthAsString = request.QueryString["month"];
      }
      JmbCalendarHelper.GetDateParts(yearAsString, monthAsString, out year, out month);
    }

The JmbCalendarHelper class herre is replacing the JmbChalk Chalk extension from the previous articles.

Back to the RenderData method above, we'll look at the PrevMonthFormat and the NextMonthFormat parameters in a moment.

Where the Name property is used The other member we must override in our widget is the Name property. This property is only shown in the Graffiti Control panel.

    public override string Name {
      get { return Title + " (JmbCalendar)"; }
    }

Another good property to override is Title, and I reference it above. The widget ancestor class has a backing store for this property so we don't have to create such a field for our descendant. The title is what appears on the sidebar as a header for the widget.

    public override string Title {
      get { return base.Title; }
      set {
        if (string.IsNullOrEmpty(value))
          base.Title = defaultTitle;
        else
          base.Title = value;
      }
    }

The defaultTitle referenced here merely has the value "Archives".

As hinted at above, I have two other properties, one for the format string for the previous month link, and one for the next month link. Obviously for these I have to have a couple of backing store fields.

    public string PrevMonthFormat {
      get { return prevMonthFormat; }
      set {
        if (string.IsNullOrEmpty(value))
          prevMonthFormat = defaultPrevMonthFormat;
        else 
          prevMonthFormat = value;
      }
    }

    public string NextMonthFormat {
      get { return nextMonthFormat; }
      set {
        if (string.IsNullOrEmpty(value))
          nextMonthFormat = defaultNextMonthFormat;
        else
          nextMonthFormat = value;
      }
    }

So far, I have three properties for my widget: the title and the two format strings. If someone — oh, I don't know, but how about Graffiti itself — were to serialize an instance of this class those would be the values saved. Handy that, especially when you ask how to change them in the widget control panel.

This is the other part of a widget that isn't well served by the documentation: the UI needed to change the widget's values. I've already pointed out that Graffiti can save and load the widget (and its configuration) to and from its internal storage using the Serializable stuff, but what about creating a UI in the control panel for the end-user to modify the values?

There are three methods you need to override, and they're all inter-related. The first method allows you to create a name/value collection to hold the widget data (the properties). The second allows you to save the edited data back into your object after the user clicked Update. The final one is a method called by the control panel to set up the UI itself to allow the editing. The UI is very simple as I'm sure you are aware: a property editor type display with one field per line.

The first method, then, is DataAsNameValueCollection(). Yeah, I know, it just trips off the tongue. It creates a name/value pair collection of the property values we wish to have edited.

    protected override NameValueCollection DataAsNameValueCollection() {
      NameValueCollection nvc = base.DataAsNameValueCollection();
      nvc["PrevMonth"] = PrevMonthFormat;
      nvc["NextMonth"] = NextMonthFormat;
      return nvc;
    }

We call the base method so that it can create a new NameValueCollection and add the title field, and then we add our two other property values. The keys I use here (PrevMonth and NextMonth) will be used again in a moment. This method will be called before the UI is created.

The next method I'll talk about is the SetValues method. This one returns the edited values back to your object. It uses a NameValueCollection again.

    public override StatusType SetValues(HttpContext context, NameValueCollection nvc) {
      StatusType result = base.SetValues(context, nvc);
      if (result == StatusType.Success) {
        PrevMonthFormat = nvc["PrevMonth"].Trim();
        NextMonthFormat = nvc["NextMonth"].Trim();
      }
      return result;
    }

Again we call the ancestor so that the title gets set, and then we set our own properties with the changed values. For this method we have to return a status value, here being success (there's not a lot that can go wrong in this class). Notice that I use the same keys as before.

Finally the AddFormElements() method. This method builds up a FormElementCollection list of controls that will be used for editing. The order of the elements in the collection determine the order in which they're displayed.

    protected override FormElementCollection AddFormElements() {
      FormElementCollection fec = base.AddFormElements();
      fec.Add(new TextFormElement("PrevMonth", "HTML for previous month link", "The HTML for the previous month link. Use {0} for the month name."));
      fec.Add(new TextFormElement("NextMonth", "HTML for next month link", "The HTML for the next month link. Use {0} for the month name."));
      return fec;
    }

My needs are simple, so I instantiated and used two TextFormElements. Notice again the PrevMonth and NextMonth keys turning up: the form code will use these to match up the editor control to the relevant data in the name/value pair collection. The call to the base method will set up the editor for the title and, obviously, create the collection object.

There are other form controls you can use (or you can write your own):

After that, it's just a case of compiling and deploying.

I hope this article has helped you understand how the widget you build interacts with the widget control panel in Graffiti CMS, and also what you need to implement and why.

I'll be making some last minute changes, adding a readme page and then uploading it all to CodePlex over the next couple of days.

Cover for Cafe del Mar, Volume 10 Now playing:
Digital Analog Band - The Blues
(from Café del Mar, Vol. 10)

Loading similar posts...   Loading links to posts on similar topics...

1 Response

#1 Two Interesting Graffiti Plugins said...
03-Feb-09 7:28 AM

We are still heads down on Graffiti 2.0 and we should some very interesting announcements in the coming

Leave a response

Note: some MarkDown is allowed, but HTML is not. Expand to show what's available.

  •  Emphasize with italics: surround word with underscores _emphasis_
  •  Emphasize strongly: surround word with double-asterisks **strong**
  •  Link: surround text with square brackets, url with parentheses [text](url)
  •  Inline code: surround text with backticks `IEnumerable`
  •  Unordered list: start each line with an asterisk, space * an item
  •  Ordered list: start each line with a digit, period, space 1. an item
  •  Insert code block: start each line with four spaces
  •  Insert blockquote: start each line with right-angle-bracket, space > Now is the time...
Preview of response