Now that a user had all the controller information set to his or her SESSION structure, Omega's next step was to lay out the page and actually draw the portal. This would require developing three elements: a template to display the tabs as links for the user, a portlet drawing mechanism which would obtain the appropriate controller for the current tab and use it, and the controllers themselves.
The tab.cfm file was a simple one: The SESSION.UserPrefs.Tabs array just needed to be looped through and each one output as a link. This link would carry a URL variable that would contain the index of the tab currently being viewed. This URL would then be set to a session variable that could be used by the portlet drawing template to obtain the controller.
This file is called portletdraw.cfm, and would invoke the getController() method of the PSML reader component. Project Omega decided that the controller would be the same name as the ColdFusion template that was the actual controller template, but minus the .cfm ending.
<CFLOCK SCOPE="SESSION" TYPE="READONLY" TIMEOUT="90"> <CFINVOKE COMPONENT="portal.components.PSMLReader" METHOD="getController" USER="#SESSION.User#" TAB="#SESSION.CurrentTab#" RETURNVARIABLE="CurrController"> </CFLOCK> <!--- check to make sure controller exists ---> <CFSET LocalPath = Server.ColdFusion.RootDir & "\wwwroot\WEB-INF\portal\lib\" & CurrController & ".cfm"> <CFIF FileExists(LocalPath)> <CFINCLUDE TEMPLATE= "/WEB-INF/portal/lib/#CurrController#.cfm"> <!--- if it doesn't exist, display default document ---> <CFELSE> <CFINCLUDE TEMPLATE="/WEBINF/ portal/lib/DefaultController.cfm"> </CFIF>
After the controller for the current tab (in the SESSION.CurrentTab variable) is retrieved, it is appended to the path where controllers are located, and the .cfm nding is added. The FileExists() function is used to make sure the controller is where it should be; if it is, it is included; if it isn't, the default controller is included.
The controllers themselves are modular units. Project Omega designed it so that other controllers could be used that offered different options for portal layouts. Two default controllers were created for this application: a one-column display of portlets and a two-column (two 50 percent width table cells) display of portlets. The single column was called CardPortletController:
<CFOUTPUT> <CFLOCK SCOPE="SESSION" TYPE="READONLY" TIMEOUT="90"> <TABLE BORDER="0" CELLPADDING="0" CELLSPACING="0" WIDTH="100%" CLASS="#SESSION.UserPrefs.Style.portletstyleclass#" STYLE="border-color:#SESSION.UserPrefs.Style. titlebackgroundcolor#"> </CFLOCK> <TR> <TD> <CFLOCK SCOPE="SESSION" TYPE="READONLY" TIMEOUT="90"> <CFLOOP FROM="1" TO="#ArrayLen(SESSION.UserPrefs.Controllers.Tabs[SESSION. CurrentTab])#"INDEX="i"> <CF_PORTLET ID="#SESSION.UserPrefs.Controllers. Tabs[SESSION.CurrentTab][i]#" DISPLAY="#SESSION.UserPrefs.Controllers.Display[SESSION. CurrentTab][i]#"TABLOCATION="#i#"> </CFLOOP> </CFLOCK> </TD> </TR> </TABLE> </CFOUTPUT>
As they worked on this template, it was clear that everything developed so far was working together. The SESSION.UserPrefs that was set up previously is used to obtain the portlets for this page. The <CF_PORTLET> tag is then executed. Also, the styles are implementedhere and in other templatesbased on the skins that Ernie set up and oversaw. Overall, once Sonic Systems' development team understood the session structure, they only needed to lay out their page in simple HTML form and then loop through the tab to output each portlet with the custom tag. And they could create controllers for any season.