Static Monthly Calendar, Part II

“Life affords no higher pleasure than that of surmounting difficulties, passing from one step of success to another, forming new wishes and seeing them gratified.”
Samuel Johnson

Now that we have the empty shell of a static monthly calendar, we can focus our attention on the integration of dynamic, use-specific content. While I have visions of occupying the entirety of the screen real estate devoted to any particular day with colors and graphics and any content that can be conceived by the provider, for now, I am going to stick with the content model that accompanied the template and focus on the mechanics of integrating the content with the calendar. At this juncture, I will leave it to the reader or some future installment to address the full potential of nature of the content, and will instead, devote this writing to the method used to deliver the content to the calendar.

My approach to integrating user-specified content with the generic calendar shell is to utilize a user-defined content provider, which is simply a Script Include that implements a specific function that takes a date as an argument and returns the content in HTML format. To create a simple example, I used the existing sample content from the original template and created a simple script that I called ExampleContentProvider:

var ExampleContentProvider = Class.create();
ExampleContentProvider.prototype = {
	initialize: function() {
	},

	getContent: function(yy, mm, dd) {
		var response = '';

		if (dd == 2) {
			response += '<div class="event">\n';
			response += '  <div class="event-desc">\n';
			response += '    Career development @ Community College room #402\n';
			response += '  </div>\n';
			response += '  <div class="event-time">\n';
			response += '    2:00pm to 5:00pm\n';
			response += '  </div>\n';
			response += '</div>\n';
		} else if (dd == 7) {
			response += '<div class="event">\n';
			response += '  <div class="event-desc">\n';
			response += '    Group Project meetup\n';
			response += '  </div>\n';
			response += '  <div class="event-time">\n';
			response += '    6:00pm to 8:30pm\n';
			response += '  </div>\n';
			response += '</div>\n';
		} else if (dd == 14) {
			response += '<div class="event">\n';
			response += '  <div class="event-desc">\n';
			response += '    Board Meeting\n';
			response += '  </div>\n';
			response += '  <div class="event-time">\n';
			response += '    1:00pm to 3:00pm\n';
			response += '  </div>\n';
			response += '</div>\n';
		} else if (dd == 22) {
			response += '<div class="event">\n';
			response += '  <div class="event-desc">\n';
			response += '    Conference call\n';
			response += '  </div>\n';
			response += '  <div class="event-time">\n';
			response += '    9:00am to 12:00pm\n';
			response += '  </div>\n';
			response += '</div>\n';
		} else if (dd == 25) {
			response += '<div class="event">\n';
			response += '  <div class="event-desc">\n';
			response += '    Conference Call\n';
			response += '  </div>\n';
			response += '  <div class="event-time">\n';
			response += '    1:00pm to 3:00pm\n';
			response += '  </div>\n';
			response += '</div>\n';
		}

		return response;
	},

	type: 'ExampleContentProvider'
};

A real content provider would, of course, use some database table or outside source to obtain the raw data from which the HTML would then be generated, but that’s pretty standard stuff that we don’t really need to get into here. Our ExampleContentProvider is simply a means to demonstrate the process of merging the dynamic content with the static calendar. How and from where you obtain your actual content is clearly up to you, but the method that we have implemented will send your process a year, a month, and a day, and will expect your process to send back the appropriate HTML for that specific day in return. You can see the process in action from these new lines in the server-side code:

if (contentProvider && contentProvider.getContent) {
	thisDay.content = contentProvider.getContent(data.year, data.month, thisDay.date);
}

The content provider is optional, so we need to check to make sure that it is there, and we also need to check to make sure that it has the required getContent method, but if all is well in that regard, we set the content for the day to the HTML returned by getContent method of the configured content provider. On the HTML side of things, then, we bind the HTML to the day using an ng-if and an ng-bind-html attribute.

<ul ng-repeat="w in data.week" class="days">
  <li ng-repeat="d in w" class="day" ng-class="{'other-month':!d.date}">
    <div class="date" ng-if="d.date">{{d.date}}</div>
    <div ng-if="d.content" ng-bind-html="d.content"></div>
  </li>
</ul>

All we need to do now is to get the user-provided content provider passed into the calendar widget somehow. This we can do with a widget option, and to make that happen, we need to add an Option Schema:

[{
    "hint": "Class Name of Script Include that provides content",
    "name": "content_provider",
    "default_value": "",
    "label": "Content Provider",
    "type": "string"
}]

Once the Option Schema is in place, clicking on the pencil icon of the widget while in the Portal Page Designer will open up the Options dialog where you can now enter the name of your content provider:

Specifying the content provider on the widget options dialog

Once specified as an option, you can pick it up in the server-side code:

if (options && options.content_provider) {
	var contentProviderName = options.content_provider;
}

Of course, you really aren’t interested in the name of the content provider … you want an actual instance of the Script Include that you can use to invoke the getContent method. For that, we need to instantiate the object using the name:

var contentProvider;
if (options && options.content_provider) {
	try {
		var ClassFromString = this[options.content_provider];
		contentProvider = new ClassFromString();
	} catch(e) {
		gs.error('Unable to instantiate Content Provider named "' + options.content_provider + '": ' + e);
	}
}

Don’t ask me to explain how all of that works, but I can tell you that it does, in fact, work. If you provide the name of an actual Script Include, it will create a usable instance of it, If you provide anything else, it logs an error, and then it goes on as if you never attempted to configure a content provider.

In addition to specifying the content provider via the Portal Page Designer, you can also specify it when including the calendar widget in another widget. In that case, the code would look something like this:

data.calendarWidget = $sp.getWidget('view-only-monthly-calendar', {content_provider: 'ExampleContentProvider'});

Either way, you have to grab the name from the options and instantiate an actual object before you can use it. Well, that’s just about it for the changes, so the next thing to do will be to bring it up and see how it all works.

Calendar with content provided by external content provider

Not bad! You will obviously want to make your own content provider and get your content from the applicable source, but the calendar widget itself should work for just about any content from any source. There are still a few cosmetic tweaks that I wouldn’t mind doing here and there, but it works, so that’s good enough for now. For those of you who might be interested, here is an Update Set.