A lot of coding blogs write about problems they have already solved and while I know its not unique to do so, I want to write down stuff as I do them for this project, rather than summarizing a problem I have already solved. Although this series of blogs involves the SCA, it will be more about the process or working on a DNN (DotNetNuke) module and the programming that goes into it.
If you are not familiar with the SCA Online OP, it is a module I wrote and maintain for the Kingdom of Atenveldt, a branch of the SCA. The OP part of the module stands for Order of Precedence. This is basically a ranking system that we use in the SCA loosely based on historical practices of knowing who is ranked above who. I have taken that practice and put a lot of work around it to not only list the people and what awards they have in the right order, but enable viewing pictures of the people, descriptions of the awards, information about the local groups the people reside in and information about the members that reign over them. All of this can be found on the Atenveldt Online OP.
Since I will have a lot of free time this memorial day weekend, I am putting some effort into some improvements I have wanted to do to the module for some time. The first improvement I want to do is to link the OP to the new forms module I just installed for making an award recommendation for a person. The forms module supports having a field get data out of a querystring, so it should be a simple task of creating a dyamic link that will point from a person's record in the OP to the award recommendation form. Even though this module will never be in widespread use and will most likely be a single installation on the Atenveldt site, I try to add features that stay flexible and have the possibility of transferring over to anther DNN installation.
Step 1. Settings.
I figure the module needs to be pointed to from the settings. I am imaging a checkbox that indicates that we want to have a reference link to the award rec. form, along with a drop down of all the installed Dynamic Forms modules installed in the portal.
I have a settings page already created. So I will add the controls to the form. Here is what the settings form looks like.
<%@ Control AutoEventWireup="false" CodeFile="SCAOnlineOPSettings.ascx.cs" Inherits="JeffMartin.DNN.Modules.SCAOnlineOP.SCAOnlineOPSettings"
Language="c#" %>
<%@ Register TagPrefix="dnn" TagName="Label" Src="~/controls/LabelControl.ascx" %>
<table>
<tr>
<td valign="top" class="SettingsLabelColumn">
<dnn:label id="dlBasePortal" runat="server" controlname="ddlPortalList" />
</td><td valign="top">
<asp:DropDownList runat="server" ID="ddlPortalList">
</asp:DropDownList>
</td>
</tr>
<tr>
<td valign="top" class="SettingsLabelColumn">
<dnn:Label ID="dlLinktoReccomendationForm" runat="server" ControlName="chkLinkToRec" />
</td>
<td valign="top">
<asp:CheckBox ID="chkLinkToRec" runat="server" />
</td>
</tr>
<tr>
<td valign="top" class="SettingsLabelColumn">
<dnn:Label ID="dlReccomendationForm" runat="server" ControlName="ddlDynamicForms" />
</td>
<td valign="top">
<asp:DropDownList runat="server" ID="ddlDynamicForms">
</asp:DropDownList>
</td>
</tr>
</table>
Now that the form fields are created, I will put some backing logic to them. First I need to fill the drop down list with all the modules that are the DataSprings Dynamic Forms that I am looking for. As I am writing this code, I realize that I want to put a message indicating that I can't detect the Dynamic Forms module being installed. This will be convenient to test as I don't have the dynamic forms module installed yet on my local build installation. As I look at the code a bit more, I decide to just hide the setting if I can't detect the module existing. So I put a placeholder module around the two table rows that contain the info needed for setting up the dynamic forms stuff.
ModuleController mc = new ModuleController();
ModuleInfo mInfo = mc.GetModuleByDefinition(PortalId, "Dynamic Forms");
if (mInfo == null)
{
phAwardRecsSettings.Visible = false;
}
else
{
phAwardRecsSettings.Visible = true;
ArrayList moduleTabs = mc.GetModuleTabs(mInfo.ModuleID);
ddlDynamicForms.DataTextField = "ModuleTitle";
ddlDynamicForms.DataValueField = "TabId";
ddlDynamicForms.DataSource = moduleTabs;
ddlDynamicForms.DataBind();
}
You may notice I am programming in C#. This is just something I would rather do than keep flipping between VB for this and C# at work. The modules work perfectly fine.
The next step for me is to try this out. I don't like going to far without making sure this code works. This keeps it fresh in my mind if I have to debug it rather than debugging something I did an hour ago if I wait that long before testing it out. After testing that nothing was different when I didn't have the dynamic forms installed, I installed the dynamic forms module, imported my form from the live website, and tested again... It works!
New Settings Display Correctly
Now that everything is displaying correctly - I need to actually make the settings save (and be read). I want to make sure the drop down box shows the correct value when the settings are loaded and the check box is correctly checked or unchecked. Here are the methods:
public override void LoadSettings()
{
try
{
if (!Page.IsPostBack)
{
var basePortalId = ((string) Settings["BasePortal"]);
if (basePortalId == null)
ddlPortalList.SelectedValue = "0";
else
ddlPortalList.SelectedValue = basePortalId;
var awardRecTabId = ((string) Settings["AwardRecTabId"]);
if (awardRecTabId != null)
ddlPortalList.SelectedValue = awardRecTabId;
bool linkToRec = Convert.ToBoolean(Settings["LinkToRec"]);
chkLinkToRec.Checked = linkToRec;
}
}
catch (Exception exc)
{
Exceptions.ProcessModuleLoadException(this, exc);
}
}
public override void UpdateSettings()
{
try
{
var objModules = new ModuleController();
objModules.UpdateModuleSetting(ModuleId, "BasePortal", ddlPortalList.SelectedValue);
objModules.UpdateModuleSetting(ModuleId, "AwardRecTabId", ddlDynamicForms.SelectedValue);
objModules.UpdateModuleSetting(ModuleId, "LinkToRec", chkLinkToRec.Checked.ToString());
Response.Redirect(Globals.NavigateURL(), true);
}
catch (Exception exc)
{
Exceptions.ProcessModuleLoadException(this, exc);
}
}
When I go to test, doh... there is a problem.
DNN Critical ErrorLets see what the Event Log has to say about that.
It turns out after quite a trip down the debugging rabbit hold that the above code doesn't compile in ASP.NET 2.0 (I may upgrade my site shortly). The 'var' keyword got slipped in there by an overzealous ReSharper reformat. After changing the settings in ReSharper, fixing the 'var', I found that the LoadSettings() actually gets called BEFORE the Page_Load... this means that I need to bind my controls in the LoadSettings(). Just because I am a bit paranoid about this changing in some future version, I put in a BindControls() method that I can call from Page_Load too. I also protect for binding twice unnecessarily. The final code for the settings is below.
using System;
using System.Collections;
using DotNetNuke.Common;
using DotNetNuke.Entities.Modules;
using DotNetNuke.Entities.Portals;
using DotNetNuke.Services.Exceptions;
namespace JeffMartin.DNN.Modules.SCAOnlineOP
{
partial class ScaOnlineOpSettings : ModuleSettingsBase
{
#region Web Form Designer generated code
protected override void OnInit(EventArgs e)
{
//
// CODEGEN: This call is required by the ASP.NET Web Form Designer.
//
InitializeComponent();
base.OnInit(e);
this.Load += new System.EventHandler(this.Page_Load);
}
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
}
#endregion
private void Page_Load(object sender, EventArgs e)
{
try
{
if (!Page.IsPostBack)
{
BindControls();
}
}
catch (Exception exc)
{
Exceptions.ProcessModuleLoadException(this, exc);
}
}
private bool boundOnce;
private void BindControls()
{
if (!boundOnce)
{
PortalController pc = new PortalController();
ArrayList Portals = pc.GetPortals();
ddlPortalList.DataTextField = "PortalName";
ddlPortalList.DataValueField = "PortalID";
ddlPortalList.DataSource = Portals;
ddlPortalList.DataBind();
ModuleController mc = new ModuleController();
ModuleInfo mInfo = mc.GetModuleByDefinition(PortalId, "Dynamic Forms");
if (mInfo == null)
{
phAwardRecsSettings.Visible = false;
}
else
{
phAwardRecsSettings.Visible = true;
ArrayList moduleTabs = mc.GetModuleTabs(mInfo.ModuleID);
ddlDynamicForms.DataTextField = "ModuleTitle";
ddlDynamicForms.DataValueField = "TabId";
ddlDynamicForms.DataSource = moduleTabs;
ddlDynamicForms.DataBind();
}
boundOnce = true;
}
}
public override void LoadSettings()
{
try
{
if (!Page.IsPostBack)
{
BindControls();
string basePortalId = ((string) Settings["BasePortal"]);
if (basePortalId == null)
ddlPortalList.SelectedValue = "0";
else
ddlPortalList.SelectedValue = basePortalId;
string awardRecTabId = ((string) Settings["AwardRecTabId"]);
if (awardRecTabId != null)
ddlPortalList.SelectedValue = awardRecTabId;
bool linkToRec = Convert.ToBoolean(Settings["LinkToRec"]);
chkLinkToRec.Checked = linkToRec;
}
}
catch (Exception exc)
{
Exceptions.ProcessModuleLoadException(this, exc);
}
}
public override void UpdateSettings()
{
try
{
ModuleController objModules = new ModuleController();
objModules.UpdateModuleSetting(ModuleId, "BasePortal", ddlPortalList.SelectedValue);
objModules.UpdateModuleSetting(ModuleId, "AwardRecTabId", ddlDynamicForms.SelectedValue);
objModules.UpdateModuleSetting(ModuleId, "LinkToRec", chkLinkToRec.Checked.ToString());
Response.Redirect(Globals.NavigateURL(), true);
}
catch (Exception exc)
{
Exceptions.ProcessModuleLoadException(this, exc);
}
}
}
}
Now that the settings are in and saved, I actually have to make use of them in the module. I will save that for Part 2.