SCA Online OP Improvements, Link to Dynamic Forms Module - Part 2

In Part 1, I did all the background programming to put the settings in and display them the correct way in the settings page, but I haven't actually used the settings to create the link yet.  I also was blogging as I was programming which I now know why this is a rarity- It takes a long time and although the side trips down a buggy path may be interesting, its distracting from getting the actual work accomplished.  I finished this up yesterday without blogging about it and it went much faster.  So this is all done now and I probably won't linger too long on any of the problems I had.

The purpose of all the settings was to enable a link to be made from my Online OP module to the Datasprings Dynamic Forms module I have set up to take Award Reccommendations.   The first part was to customize this form a bit more so that it would even recognize this data.  The Dynamic Forms Module allows for data to come in in several ways, cookie, session, and querystring.   I decided to use the querystring to send this info forward.  In the advanced options of the question I found the setting I was looking for.

I set this for the Name and Branch field of the Awards Form and I also set up a hidden field for the internal member id.  I would use this to pass the id forward so it could link to the person in the OP.

Once the fields were set up, I modified the email result so that it could contain a link directly to the person that was being recommended.  The form completion events have 2 events, one to send an email to the crown, the other to send it to the person submitting a recommendation.  Both of these would recieve the link.   Each already had the main email set up so I only had to add the link at the bottom of the existing email text.  The main trick (if you can call it a trick) was to put the $(memid) field into the link target.  $(memid) was the hidden field I had set up to read a querystring I was sending.

Now to set up the Online OP module to provide the link.  The first thing I did was put the link in near the Awards header in the OP Record screen.

 

<h2 class="SCAMemberAwardsHeader">
        Awards Received</h2>
    <asp:Literal ID="litNoAwards" runat="server" Visible="false">No Awards Recorded.<br /></asp:Literal>
    <asp:HyperLink runat="server" ID="hlRecommend" CssClass="RecommendLink" >Recommend this person for an award.</asp:HyperLink>
    <div class="break"></div>
    <asp:Panel ID="pnlAwards" runat="server" Visible="true">
        <asp:DataGrid ID="dgAwards" runat="server" AutoGenerateColumns="False" CssClass="ResultList">

 

I also set up the link so it would float off to the right, under the picture of the person if there is one but inline with the Awards header.  This is from the module.css

 

.RecommendLink
{
    display:block;
    float:right;
    margin:15px 0px 15px 0;
}

 

 Next I had to generate the link.  I checked the settings to see if I should generate the link and then if I should, which tab should I direct to.  The querystring key's are determined by the short name of the fields in the Dynamic Forms module.

 

if(Convert.ToBoolean(Settings["LinkToRec"]))
            {
                hlRecommend.NavigateUrl = Globals.NavigateURL(Convert.ToInt32(Settings["AwardRecTabId"]), "",
                                                              "Recommended=" + h1MemberName.InnerText, "memid=" + memid, "branch=" + Residence);
            }
            else
            {
                hlRecommend.Visible = false;
            }

 

I initially had some trouble with the Friendly Url Provider I was using (iFinity Friendly Url Provider) and the way it was handling some special characters such as commas and single quotes (apostrophes), but this was resolved by using the latest dll provided on the site for DNN 5. 

Writing this blog after the fact is definately the way to go. Its like providng your own code review, which is very handy on a project you work on by yourself.

 

SCA Online OP Improvements, Link to Dynamic Forms Module - Part 1

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.