Dashboard > Community Wiki > ... > Template Examples > Event calendar
Event calendar Log In View a printable version of the current page.

Added by GrĂ©gory Joseph , last edited by GrĂ©gory Joseph on May 20, 2008  (view change)
Labels: 

I did a bit of interesting coding recently that I thought might be beneficial to others. (Some of this is based on the work at Content of children pages sorted in the parent page.) This isn't enough that you can copy & paste outright to get it working, but illustrates some interesting concepts that may be of use as you build something similar.

Business Requirement:

The site manager should be able to post several dozen events to an "Upcoming Events" section of the site at one. As time passes, the display logic should choose 2 of those to actually render on the public site. Those two events are chosen based on what is coming up soonest, but hasn't yet passed. When in authoring mode, all of the entered events should be visible and sorted chronologically. When in preview mode or on a public instance, only the selected 2 should be rendered.

The Code:

First off, we have to define a comparator to allow us to sort the Content items according to the event's start date. You can do this right in the JSP so that there's no special deployment required.

<jsp:declaration>
	public class StartDateComparator implements java.util.Comparator {
	    public int compare(Object o1, Object o2) {
	    Content c1 = (Content) o1;
	    Content c2 = (Content) o2;
	    java.lang.Long s1 = new Long( c1.getNodeData("startDate").getDate().getTimeInMillis() );
	    java.lang.Long s2 = new Long( c2.getNodeData("startDate").getDate().getTimeInMillis() );
	    return s1.compareTo( s2 );	    }
	}
</jsp:declaration>

Next, we set up a Rule object that we use to filter the content to just the mgnl:content nodes and do some other initializing:

<jsp:scriptlet>
	Rule rule = new Rule();
	rule.addAllowType( "mgnl:contentNode" );
	RuleBasedContentFilter filter = new RuleBasedContentFilter( rule );

	Content thisPage = Resource.getActivePage(request);
	Iterator i;
	int displayCount;
</jsp:scriptlet>

Now that all of the setup is done, we can actually get the iterator with the sorted events (which are Content objects) and begin to work our way through them.

Interesting bits:

  • The Content.getChildren() method allows us to pass the RuleBasedContentFilter we'd previously defined and an instance of StartDateComparator to get back the results we want.
  • By putting eventContentNode into a request attribute, we can use the <cms:setNode> tag later on to make the attributes of the object available to JSTL.
  • Because Magnolia stores our date with a Gregorian Calendar, we have to do a getTime() on that object before we can compare it to the current Date.
  • We use Server.isAdmin() to check whether we're on an admin instance, and Resource.showPreview( request ) to see whether we're in preview mode or not.
  • We'll increment displayCount later on whenever we render an event so that we can keep track of how many have actually been shown.

Here's the code:

<jsp:scriptlet>
	i = thisPage.getChildByName("spotlightEvents").getChildren( filter, new StartDateComparator() ).iterator(); 
	displayCount = 0;
	while ( i.hasNext() ) {
		Content eventContentNode = (Content)i.next();
		request.setAttribute( "eventContentNode", eventContentNode );
		boolean isPast = ( eventContentNode.getNodeData("endDate").getDate().getTime().before( new java.util.Date() ) );
		if ((Server.isAdmin() && !Resource.showPreview(request))
                    || ( !isPast && displayCount < 2)
                ) {
</jsp:scriptlet>

Next up, we have some logic to wrap the event in a table for layout (I know, I know, it's old layout code) and to display the admin controls. Interesting bits:

  • Because we're not using <cms:contentNodeIterator>, we have to specify the contentNodeName and paragraph for <cms:editBar>.
  • Setting the moveLabel of <cms:editBar> to "" removes the move button from the editBar. (We remove it here because the results are sorted, so moving an event manually doesn't make any sense.)
  • We use <cms:setNode> to cram the event Content item into a Map called event so that we can use its attributes easily in JSTL. Further, we use request scope so that any other pages we call for rendering will have access to that data as well.

The code:

<table border="0" cellpadding="0" cellspacing="0"
	class="spotlights">
	<tr>
		<td>
			<cms:adminOnly>
				<cms:editBar moveLabel="" contentNodeCollectionName="spotlightEvents"
				  contentNodeName="${eventContentNode.name}" paragraph="main-site-spotlight"/>
			</cms:adminOnly>
			<cms:setNode var="event" scope="request" content="${eventContentNode}" />
			
			<cms:includeTemplate contentNode="${eventContentNode}" />
		</td>
	</tr>
</table>
<jsp:scriptlet>
		displayCount++;
	}
}
</jsp:scriptlet>

Here's a bit of the rendering code from the paragraph definition.

  • We don't use a <cms:setNode> here, as we already used it in the template and put the data into the request so we could get to it here as well.
  • JSTL's formatting tags come in useful for making the date look the way we want it to.
<img class="featuredEventPhoto" height="75px" width="75px" align="left"
	src="${pageContext.request.contextPath}${event.image}"
	alt="${event.title}" />

<fmt:formatDate value="${event.startDate.time}" pattern="EEE, MMMM d" var="formattedStartDate" />
<fmt:formatDate value="${event.endDate.time}" pattern="EEE, MMMM d" var="formattedEndDate" />

<c:set var="formattedDate">
	<c:choose>
		<c:when test="${ formattedStartDate eq formattedEndDate }">
			${ formattedStartDate }
		</c:when>
		<c:otherwise>
			${ formattedStartDate } - <br/>$\{ formattedEndDate }
		</c:otherwise>
	</c:choose>
</c:set>

<c:choose>
	<c:when test="${!empty(event.link)}">
		<a href="${event.link}"><b>${formattedDate}<br />${event.title}</b>
		</a>
	</c:when>
	<c:otherwise>
		<b>${formattedDate}<br />${event.title}</b>
	</c:otherwise>
</c:choose>


<div class="eventdescription">
	${event.text}
</div>

Finally, back in the template, we increment our display count and wrap up the loop:

<jsp:scriptlet>		displayCount++;	}
}
</jsp:scriptlet>

Note:

Because this code has to be called to work, you will have to have some mechanism to clear the cache periodically for this page, or just turn the cache off altogether. (Otherwise, the page content gets cached the first time and doesn't get reevaluated thereafter.) You may find Configuration->Cache->URI->Deny helpful for this purpose.

Powered by a free Atlassian Confluence Open Source Project License granted to Magnolia International. Evaluate Confluence today.
Powered by Atlassian Confluence 2.7, the Enterprise Wiki. Bug/feature request - Atlassian news - Contact administrators