This Book Will Not Make You A Good Designer

One of the biggest stigmas that DotNetNuke lives with is the belief that an attractive web site and a DNN-based web site cannot be one in the same. Many people think that if your web site runs on DotNetNuke, then it will inherently look bad. This, of course, is a fallacy.

However, as soon as a web site owner takes the time to learn the ropes of constructing a custom DotNetNuke skin, they likely jump the gun and create a skin that ends up looking no better than a $20 skin off Snowcovered, or even the default DotNetNuke skin. This is due to one important detail that is continuously overlooked: skinning is mostly web design, and thus if you have no design skills, then taking the time to learn how to construct a skin is simply setting yourself up for disappointment. Anyone can learn to make a skin, but only someone with design experience can make a skin that is aesthetically pleasing.

So, with that in mind...

 

image

 

The Good Qualities Of This Book

I've found a lot of wrongful confusion and intimidation surrounding the topic of creating custom DotNetNuke skins. The process of skinning DotNetNuke is pretty straight forward. If you have clear instructions then there should be no confusion. What I love most about this book is that it shows you just how quick and easy it is to make your own skin. In Chapter two, the author steps you through creating a skin from scratch in "in record time," and proves to the reader that DotNetNuke skinning is not as hard as it seems. By the end of the second chapter, a reader with no programming experience can have a skin developing environment set up, and can have their very first skin created. Now I think that is pretty rad!

One observation that I have had about [PACKT] books is that they like to keep things simple. I think a lot of non-technical minded people can get bored quickly when they pick up a thick book that is full of technical jargon. In the case of explaining DotNetNuke skinning, very little technical jargon is required. The author of this book has recognized this fact, and has thusly kept with the idea of keeping the content simple.  On the other hand, all of the essential steps of DotNetNuke skinning are covered in this book. So, I feel like I can hand this book to one of my designers and more or less know that they will be able to read through it and then begin making me beautiful skins.

Now for those of us that are already familiar with skinning DotNetNuke, we might wonder what is in this book for us. I will admit that experienced skinners will find the content in this book extremely light. However, I personally did pick up a few tidbits of knowledge and some clever ideas from the author. For instance, I learned that the style sheet editor in the DotNetNuke portal menu actually uses the portal.css file to populate all the empty selector tags it displays by default. I also read about alternative ways to go about performing different tasks in the skinning process.

Above all, I felt that the author brought to light several things that I had seen before in other DotNetNuke documentation, but never made the effort to think critically about. Reading this book made me reconsider how several of the skinning concepts fits into the overall process. I now have a more complete understanding of those concepts, and maybe because of that I will try to use them to a more full extend in the next skins I create.

The Maladies

Few books are perfect. This one isn't, you can be sure of it. I was expecting to learn some advanced skinning techniques, and to gain some deep understanding of the skinning process. But that's not what this book is all about. As I stated in the Qualities section of this post, this book is meant to be a quick and easy tutorial. So, you really can't criticize it for the lack of technical details.

That said, there are a few things that bugged the crap out of me. For one, this short 143 page book has almost 40 pages (chapter 7) of content that has nothing exclusively to do with DotNetNuke skinning. There is information varying from how to make rounded corners in Photoshop to where you can get free web design layouts on the Internet. It is really B-Roll material that makes me wonder if the book is really worth $34.99 USD.

I also wasn't very happy with the section on creating custom containers. The author gave good examples in most of the other sections, but when it came to the chapter on containers, he really fell short. I would have traded all 40 pages of chapter 7 for a page or two more giving me a better explanation and examples of container creation. The same can be said for the chapter on packaging and deploying of DotNetNuke skins. It is weak.

Why Don't You Get Your Skinning On

If you're new to DNN skinning all together, then I think this book might be a good place to start. You can grab a copy off Amazon.com by clicking here. Or, get it directly from the Packt web site.

And for those of us that are never satisfied with one source of knowledge, here are a few more resources to fulfil your information fetish:

Overall, what makes a good DotNetNuke skinner is good design skills. So if you try your hand at skinning DNN and your skin turns out looking like poop on a canvas — don't blame DotNetNuke. You either need to have good design skill yourself, or you need to budget the time to have a professional designer do the work for you.

I hope you've enjoyed my thoughts on the new book on DotNetNuke skinning. Please post information about good skinning resources if you know of any.

 

Technorati Tags: ,,,

image

I finally found some extra time to create a screen cast demonstrating the method I use to start a new module project. The object of this method is to get a basic module up and running within a DotNetNuke installation, as quickly as possible. You can check out the screen cast here. I hope you enjoy!

Now that you've seen the method that I use, I'd like to read about how you go about constructing your modules. If you have a moment, please post a comment describing your module development process.

It turns out that I am not the only person that has ever needed to conditionally hide or show a DotNetNuke module instance. Reader Baldwin emailed me wanting to know how I handle the situation where a module instance needs to be removed from a page, but still persist in the database. Here I will show you exactly that.

PortalModuleBase.ContainerControl

As you might recall, all DotNetNuke modules inherit from the PortalModuleBase class, which is found in the DotNetNuke.Entities.Modules namespace. One of the properties that your module inherits is ContainerControl. The ContainerControl property has a getter method that finds and returns the Control that contains your module's instance. In particular, it finds the control named "ctrYourModuleID", where "YourModuleID" is a unique identifier for the module instance:

 

Public ReadOnly Property ContainerControl() As Control
    Get
        Return FindControlRecursive(Me, "ctr" & ModuleId.ToString)
    End Get
End Property

 

The Control that ContainerControl gives you is actually the <div> that renders around your container when the page is sent to the browser. Here is an example:

 

image

 

ContainerControl.Visible

Since this is a regular Control, it has a Visible property which when assigned a value of false will prevent the Control, and all of its children controls from rendering out to the browser. In other words, hiding your module instance is as simple as setting the Visible property on its ContainerControl to false. Just insert this line in the appropriate location of your module code:

ContainerControl.Visible = false;

 

That was simple!

Working Example:

Here is quick module that I cooked up to demonstrate this. The following module is visible when the module is in edit mode, but is hidden when the module is in regular view mode. To switch between these modes you must be logged in as an administrator and then use the mode radio buttons in the top control panel:

image

Files:

ToggleVisibilityCS.ascx.cs - C# code-behind for the module

using System;
using System.Web.UI;

using DotNetNuke.Entities.Modules;

namespace Kemmis.Examples.DotNetNuke
{
    public partial class ToggleVisibilityCS : PortalModuleBase
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!Page.IsPostBack)
            {
                if (!IsEditable)
                {
                    // hide module + container when not in edit mode
                    ContainerControl.Visible = false;
                }
            }
        }
    }
}

ToggleVisibilityCS.ascx - xml for the module

<%@ Control Language="C#" AutoEventWireup="true" 
    CodeFile="ToggleVisibilityCS.ascx.cs"
    Inherits="Kemmis.Examples.DotNetNuke.ToggleVisibilityCS" %>
<h2>
    This module is visible.</h2>
<p>
    Switch to View mode in the control panel to make this module invisible.</p>

 

In a previous article I showed you how to write a custom DotNetNuke SchedulerClient (also known as a Scheduled Task). In this follow up article I will show you how to add, remove, and update your new SchedulerClient within your DotNetNuke installation programmatically, so that you can automate the installation of your custom SchedulerClient. Using this method you can avoid having to manually configure schedule items from within your DotNetNuke site's Host > Schedule menu.

The DotNetNuke.Services.Scheduling.SchedulingProvider Class

If you are going to be programmatically modifying schedule items in a DotNetNuke installation you are going to want to get to know the SchedulingProvider class. Unlike many of the other facilities that the DotNetNuke Framework provides, there does not exist a controller class that handles the dirty work for managing the scheduled items. Instead, the DotNetNuke Framework relies on a provider model for managing the scheduled task system.

I won't go too far into the details of the provider model, but in essence  this design pattern prevents the DotNetNuke Framework from becoming dependent on one particular class. So, in theory, the implementation of the provider can be swapped out with another, without any change to the DotNetNuke core code. Such is the case with the SchedulingProvider class.

The DotNetNuke.Services.Scheduling.SchedulingProvider class has two primary purposes. The first job of this class is to define all the methods that an implementation of a scheduling provider must produce. SchedulingProvider acts as an abstract class from which all concrete provider implementation must inherit.  Here is a screen shot of the methods, properties, and fields which the abstract SchedulingProvider class defines:

image

Notice that there are a few methods that we are likely to care about. For instance the AddSchedule(), DeleteSchedule(), and UpdateSchedule() methods define the behaviors for which a concrete provider will be able to add, delete, and update schedule items. Also note that there is an odd method named Instance() that returns a SchedulingProvider object. This method provides the second job of the SchedulingProvider class.

The SchedulingProvider.Instance() Method

The second role of the SchedulingProvider class is to provide access to the one, singleton, instance of the concrete SchedulingProvider. When the DotNetNuke Framework executes, one and only one instance of the concrete SchedulingProvider is loaded into memory. This is the instance that you must use in order to access and modify schedule items.

So, you may ask, how do you access this singleton instance of the concrete SchedulingProvider? You get it by calling the SchedulingProvider.Instance() method.

The SchedulingProvider.Instance() method is a static method, meaning you call it on the class itself, rather than calling it on an instance of the class. So you can get a reference to the singleton like this if you need to:

SchedulingProvider singleton = SchedulingProvider.Instance();

You can alternatively call methods directly on the singleton without creating a new variable. Like so:

SchedulingProvider.Instance().MethodName();

Where MethodName() is the name of the method you wish to execute on the singleton.

So in summary, the SchedulingProvider class is an abstract class that defines the management methods necessary for schedule management. All concrete SchedulingProvider classes must inherit from the SchedulingProvider class and override all the appropriate methods. The way to access the instance of the functioning concrete SchedulingProvider is to call the Instance() method on the abstract SchedulingProvider class.

Moving On

Let's take a look at the information that you are really here for: How to manage Schedule Items programmatically.

As I hinted at previously, you will be calling methods on the SchedulingProvider instance in order to add, remove, and update schedule items. All of these methods take as a parameter an instance of the ScheduleItem class. So let us take a look at the ScheduleItem class for a moment.

The DotNetNuke.Services.ScheduleItem Class

The ScheduleItem class is fairly straight forward. This class represents a single entry that you see within your DotNetNuke Host > Scheduling control panel section.

image

image

Each property in this class simply wraps a setting for the entry in the scheduling system. These properties also each map to a column in the Schedule database table.

image

As you might recall from my previous article, the scheduled task settings hash table is not fully implemented as of DotNetNuke 4.8. So avoid calling the AddSetting() method on the ScheduleItem class unless you enjoy seeing exceptions thrown in your module. Maybe someone can create a request in the DNN BugTracker  to finish this feature.

Adding a new Schedule Item

Now that you understand all the players in the game, we can add a new Schedule Item programmatically. First you create an instance of ScheduleItem, and populate its properties like so:

ScheduleItem item = new ScheduleItem();

// fully quallified class name and binary name
item.TypeFullName = _TypeFullName;

// execution frequency 
item.TimeLapse = 5;

// units of TimeLapse - "s" for seconds, "m" for minutes, "h" for hours, "d" for days
item.TimeLapseMeasurement = "m";

// retry fequency
item.RetryTimeLapse = 10;

// unit of RetryTimeLapse - "s" for seconds, "m" for minutes, "h" for hours, "d" for days
item.RetryTimeLapseMeasurement = "s";

// num of schedule history events to keep
item.RetainHistoryNum = 50;

// run on event, for instance "APPLICATION_START" or "None"
item.AttachToEvent = "None";

// enable / disable catch up
item.CatchUpEnabled = false;

// enable the scheduled task itself
item.Enabled = true;

// add object dependencies - for example "SiteLog,Users,UsersOnline"
item.ObjectDependencies = "";

// define which servers task should run on - (this is optional of course)
item.Servers = "";

Then you call the AddSchedule() method on the ScheduleProvider instance, passing in the new ScheduleItem:

SchedulingProvider.Instance().AddSchedule(item);

You will now have a new Schedule Item in your DNN scheduling system.

Removing a Schedule Item

Removing a schedule item is just as simple as adding one. You first need to get an instance of a ScheduleItem that contains the ID of the ScheduleItem that you wish to remove. You can do this two ways:

ScheduleItem item = SchedulingProvider.Instance().GetSchedule(id);

or

ScheduleItem item = new ScheduleItem();
item.ScheduleID = id;

In either case you need to know the id of the ScheduleItem to remove. If you don't know the id, you can get an ArrayList of all the ScheduleItems in the DNN installation by calling the GetSchedule() method on the SchedulingProvider instance, without passing any parameters. You can then examine each of the ScheduleItems individually to determine which is the one you need to remove.

Finally, to remove the ScheduleItem you need to pass the ScheduleItem instance to the DeleteSchedule() method of the SchedulingProvider instance.

private int GetIDOfScheduleClient(string typeFullName)
{
    // get array list of schedule items
    ArrayList schduleItems = SchedulingProvider.Instance().GetSchedule();

    // find schedule item with matching TypeFullName
    foreach (object item in schduleItems)
    {
        if (((ScheduleItem)item).TypeFullName == typeFullName)
        {
            return ((ScheduleItem)item).ScheduleID;
        }
    }
    return -1;
}

Updating a Schedule Item

To update a schedule item you call the UpdateSchedule() method on the SchedulingProvider instance and pass an instance of the updated ScheduleItem. The example below gets a ScheduleItem from the provider instance, modifies it, and submits the changes back to the provider:

// get the item by id
ScheduleItem item = SchedulingProvider.Instance().GetSchedule(id);

// set property on item
item.TimeLapse = 60;

// update item
SchedulingProvider.Instance().UpdateSchedule(item);

In Summary

In summary, you can add, remove, and update ScheduleItems by calling the AddSchedule(), DeleteSchedule(), and UpdateSchedule() methods respectively on the singleton instance of the SchedulingProvider. In each case you need to pass an instance of ScheduleItem that has been populated with the appropriate data.

Please comment or contact me if you have any questions or would like to add some additional knowledge that is missing from this article.

Working Examples

Here is a working module for your reference. This module installs the ScheduledTaskExample SchedulerClient that can be found in this post. You will need to have the appropriate SchedulerClient dll file in your bin directory in order to run this module.

ScheuleClientInstallerCS.aspx:

<%@ Control Language="C#" 
    AutoEventWireup="true" 
    CodeFile="ScheduleClientInstallerCS.ascx.cs"
    Inherits="Kemmis.Examples.DotNetNuke.ScheduleClientInstallerCS" %>
<div style="text-align: center">
    <asp:Button ID="Button1" 
        runat="server" 
        Text="Install ScheduledTaskExample" 
        OnClick="Button1_Click"
        CommandArgument="add" />
</div>

ScheduleClientInstallerCS.aspx.cs

using System;
using System.Collections;
using System.Web.UI.WebControls;

using DotNetNuke.Entities.Modules;
using DotNetNuke.Services.Scheduling;

namespace Kemmis.Examples.DotNetNuke
{
    public partial class ScheduleClientInstallerCS : PortalModuleBase
    {
        private const string _TypeFullName = "Kemmis.Examples.DotNetNuke.ScheduledTaskExample,DotNetNukeScheduledTaskExampleCS";

        protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack && GetIDOfScheduleClient(_TypeFullName) > 0)
            {
                Button1.CommandArgument = "delete";
                Button1.Text = "Delete ScheduledTaskExample Item"; 
            }
        }

        protected void Button1_Click(object sender, EventArgs e)
        {
            Button btn = (Button)sender;
            switch (btn.CommandArgument)
            {
                case "add":
                    InstallScheduleClient();
                    btn.CommandArgument = "update";
                    btn.Text = "Update ScheduledTaskExample Item";
                    break;
                case "update":
                    UpdateScheduleItem(GetIDOfScheduleClient(_TypeFullName));
                    btn.CommandArgument = "delete";
                    btn.Text = "Delete ScheduledTaskExample Item";
                    break;
                case "delete":
                    RemoveScheduleClient(GetIDOfScheduleClient(_TypeFullName));
                    btn.CommandArgument = "add";
                    btn.Text = "Install ScheduledTaskExample Item";
                    break;
            }
        }

        private void InstallScheduleClient()
        {
            ScheduleItem item = new ScheduleItem();
            
            // fully quallified class name and binary name
            item.TypeFullName = _TypeFullName;
            
            // execution frequency 
            item.TimeLapse = 5;
            
            // units of TimeLapse - "s" for seconds, "m" for minutes, "h" for hours, "d" for days
            item.TimeLapseMeasurement = "m";

            // retry fequency
            item.RetryTimeLapse = 10;

            // unit of RetryTimeLapse - "s" for seconds, "m" for minutes, "h" for hours, "d" for days
            item.RetryTimeLapseMeasurement = "s";

            // num of schedule history events to keep
            item.RetainHistoryNum = 50;

            // run on event, for instance "APPLICATION_START" or "None"
            item.AttachToEvent = "None";

            // enable / disable catch up
            item.CatchUpEnabled = false;

            // enable the scheduled task itself
            item.Enabled = true;

            // add object dependencies - for example "SiteLog,Users,UsersOnline"
            item.ObjectDependencies = "";

            // define which servers task should run on - (this is optional of course)
            item.Servers = "";

            // add item
            SchedulingProvider.Instance().AddSchedule(item);
        }

        private void RemoveScheduleClient(int id)
        {
            // get the item by id
            ScheduleItem item = SchedulingProvider.Instance().GetSchedule(id);

            // tell the provider to remove the item
            SchedulingProvider.Instance().DeleteSchedule(item);
        }

        private void UpdateScheduleItem(int id)
        {
            // get the item by id
            ScheduleItem item = SchedulingProvider.Instance().GetSchedule(id);

            // set property on item
            item.TimeLapse = 60;

            // update item
            SchedulingProvider.Instance().UpdateSchedule(item);
        }
        private int GetIDOfScheduleClient(string typeFullName)
        {
            // get array list of schedule items
            ArrayList schduleItems = SchedulingProvider.Instance().GetSchedule();

            // find schedule item with matching TypeFullName
            foreach (object item in schduleItems)
            {
                if (((ScheduleItem)item).TypeFullName == typeFullName)
                {
                    return ((ScheduleItem)item).ScheduleID;
                }
            }
            return -1;
        }

    }
}

 

image

Thanks to everyone that has submitted feedback so far.  If you haven't entered the contest yet, you still have time to do so. Send me feedback about this blog by the end of the day for a chance to win. Please see this post for more details. I will be announcing the winner tomorrow and sending out the copy of Visual Studio 2008 immediately following the receipt of shipping information.

Thanks again—and good luck!!

image

I am giving away a free copy of Visual Studio 2008 Standard Edition to one of the lucky readers of this blog. In order to be eligible for the drawing you simply need to give me some constructive feedback. Fill out this contact form and include answers to these three questions in your message:

  • What qualities (if any) distinguish my blog from the other blogs that you frequent?
  • In what ways can I improve my blog / blog posts?
  • What topic would you most like to read about in the near future?

Get your feedback in by April 28th. I will be randomly choosing one person on the 29th for the free copy of Visual Studio 2008 Standard Edition. Good luck and thanks for the feedback!

If you are searching for instructions on how to write your own DotNetNuke scheduled task, then you've come to the right blog post. This article discusses the steps that I take in order to write a task that I need DotNetNuke to perform periodically for me.

DotNetNuke has several little-known gems that can add some powerful functionality to your web site. Scheduled tasks are one of those gems that a lot of people don't know about, or at least don't take advantage of. As we've seen in the past, the documentation on the DotNetNuke web site is either out of date, incorrect, or incomplete. So to remedy this, and hopefully get more people writing their own scheduled task, I'm going to show you how I go creating a custom scheduled task. I'll also share some working examples that will help you get started quickly.

On with the show.

 

Start Using DLLs

As far as I am aware, scheduled tasks need to be compiled into a DLL file. This seem to be implied in the document, though I haven't seen anywhere that explicitly states this. If you are already building DNN modules using the Web Application Project (WAP) method, then all you need to do is create your scheduled task inside your normal project. WAPs will compile nicely up into one or more DLL files without any fuss.

If you haven't taken the jump to using WAP, and you're using WST (Web Site Project), or you don't write custom DNN modules at all, then you have the option of creating your scheduled task inside its own Class Library project. You will need Visual Studio 2005 or 2008 to do this. Class Library projects compile up into a DLL file quite nicely. If you are using VB, make sure that you sent the root Namespace of your project to blank. This is KEY! You will also need to add a reference to the DotNetNuke.dll to your Class Library project. DotNetNuke.dll can be found in the bin directory of your DotNetNuke installation.

If you don't have access to Visual Studio 2005 or 2008, and are using Visual Web Developer Express to write DNN modules, then you might be out of luck. Visual Web Developer Express doesn't compile into DLLs for distribution. However, you might want to look into using Visual Studio Express... it might be able to compile to DLL. Perhaps one of the other readers can suggest a method.

 

Sub-Class From SchedulerClient

Once you have nailed down a method for rolling your code into a DLL file, you can begin writing your scheduled task. All of your scheduled tasks begin by sub-classing from the DotNetNuke.Services.Scheduling.SchedulerClient class.

Example - C#:

class ScheduledTaskExample : SchedulerClient

Example - VB:

Public Class ScheduledTaskExample
    Inherits SchedulerClient

 

Overload Your Class Constructor

The next thing that DotNetNuke requires from a scheduled task is an overloaded constructor that accepts a DotNetNuke.Services.Scheduling.ScheduleHistoryItem object. This overloaded constructor must also call the constructor of its parent class. This is done by using the base() syntax in C#, and the MyBase.New() syntax in VB. The final thing that your constructor must do is assign the ScheduleHistoryItem parameter to the ScheduleHistoryItem property of the class. Here is an example of a constructor that meets these requirements:

Example - C#:

public ScheduledTaskExample(ScheduleHistoryItem objScheduleHistoryItem)
    : base()
{
    ScheduleHistoryItem = objScheduleHistoryItem;
}

Example - VB:

Public Sub New(ByVal objScheduleHistoryItem As ScheduleHistoryItem)
    MyBase.New()
    Me.ScheduleHistoryItem = objScheduleHistoryItem
End Sub

 

Override DoWork() Method

The DoWork() method is the entry point for the execution of your scheduled task. When the DotNetNuke portal determines that it is time for your scheduled task to run, it creates an instance of your class and calls its DoWork() method. So this is where you put all your custom functionality.

There are a few things to keep in mind when writing this method. First, you need to include logic that alerts the portal to whether or not your task succeeded at whatever it needed to do. The way you do this is by enclosing your code within a try-catch block. If your code runs as planned, then you set the Succeeded property of the ScheduledHistoryItem member to true. Otherwise, you set the Succeeded property to False. In the catch portion of your try-catch block you will also want to set the Succeeded property to False so that when an exception is thrown, the scheduled task will report that it failed.

Example - C#:

public override void DoWork()
{
    try
    {
        // do some stuff ...

        // then report success to the scheduler framework
        ScheduleHistoryItem.Succeeded = true;
    }

    // handle any exceptions
    catch (Exception exc)
    {
        // report a failure
        ScheduleHistoryItem.Succeeded = false;

        // log the exception into
        // the scheduler framework
        ScheduleHistoryItem.AddLogNote("EXCEPTION: " + exc.ToString());

        // call the Errored method
        Errored(ref exc);

        // log the exception into the DNN core
        Exceptions.LogException(exc);
    }
}

Example - VB:

Public Overrides Sub DoWork()
    Try
        ' do some stuff ...


        ' then report success to the scheduler framework
        ScheduleHistoryItem.Succeeded = True

    Catch ex As Exception
        ' handle any exceptions

        ' report a failure
        ScheduleHistoryItem.Succeeded = False

        ' log the exception into
        ' the scheduler framework
        ScheduleHistoryItem.AddLogNote("EXCEPTION" + ex.ToString())

        ' call the Errored method
        Errored(ex)

        ' log the exception into the DNN core
        Exceptions.LogException(ex)

    End Try
End Sub

 

You will notice in the examples above that there are a few other things you should do when an exception occurs. You should log the exception into the scheduler framework which allows you to view the exception in your scheduler's history page. This is done by calling the AddLogNote() method on the ScheduleHistoryItem member. Secondly, you need to call the parent class's Errored() method so it can take its own necessary actions. Lastly, you should log the exception into the DNN core exception log by calling the LogException method on the DotNetNuke.Services.Exceptions.Exceptions class.

 

Compile & Install

Once you are finished overriding the DoWork() method you are ready to compile and install your new scheduled task. Compiling instructions will vary depending upon what tool and project type you are using, so I will not discuss those details here. Once you are compiled up into a DLL file you will want to copy the DLL file into the bin directory of your DNN Web site installation.

To install your new task you go into Host menu > Schedule  page and click on the Add Item to Schedule link.

 

image

 

Then enter the full class name, followed by a comma, followed by the name of your DLL. Like so:

 

image

 

Configure it up, hit Update, and you should be good to go.

 

Some Tips

Since scheduled tasks do not run in the context of a Web page execution, nor in the context of any particular DotNetNuke portal, you will often need to find alternative ways to access the information that your task requires. Instead of Server.Globals.MapPath() use System.Web.HttpRuntime.AppDomainAppPath to get the file path to your DNN installation. If your task uses some of the DNN framework methods that require you to pass the ID of a portal, then you can either use a PortalController to get all of the portal IDs and loop through them, or you can access particular portal IDs by storing and retrieving them using System.Configuration.ConfigurationSettings.AppSettings. AppSettings allows you to store configuration information inside the .config files within the .Net configuration hierarchy. Likewise for tab IDs and module IDs, you may have to manually store and retrieve these from in a .config file.

Another thing to keep in mind is that the Add/GetSetting methods on the ScheduleHistoryItem class really suck. You might be tempted to use it, but you'll quickly be scratching your head because there are no easy ways to update an existing setting. So you will need to find an alternate method to store settings for your scheduled task.

 

Working Examples!!!

Here is an example of a working scheduled task. It writes out a file to the root folder of your web application.

C# Version - ScheduledTaskExample.cs:

using System;
using System.IO;
using System.Web;

using DotNetNuke.Services.Scheduling;
using DotNetNuke.Services.Exceptions;

namespace Kemmis.Examples.DotNetNuke
{
    class ScheduledTaskExample : SchedulerClient
    {
        public ScheduledTaskExample(ScheduleHistoryItem objScheduleHistoryItem)
            : base()
        {
            ScheduleHistoryItem = objScheduleHistoryItem;
        }

        public override void DoWork()
        {
            try
            {
                // perform some task                
                String strPath = HttpRuntime.AppDomainAppPath + "CS_DID_IT.TXT";
                using (StreamWriter sw = new StreamWriter(strPath, true))
                {
                    sw.WriteLine(DateTime.Now.ToString() + " - C# DID IT!");
                    sw.Close();
                }

                // report success to the scheduler framework
                ScheduleHistoryItem.Succeeded = true;
            }
            catch (Exception exc)
            {
                ScheduleHistoryItem.Succeeded = false;
                ScheduleHistoryItem.AddLogNote("EXCEPTION: " + exc.ToString());
                Errored(ref exc);
                Exceptions.LogException(exc);
            }
        }
    }
}

 

VB Version - ScheduledTaskExample.vb:

Imports DotNetNuke.Services.Scheduling
Imports DotNetNuke.Services.Exceptions

Imports System
Imports System.Web
Imports System.IO

Namespace Kemmis.Examples.DotNetNuke

    Public Class ScheduledTaskExample
        Inherits SchedulerClient

        ' requires a special constructor which
        ' accepts a ScheduleHistoryItem 
        Public Sub New(ByVal objScheduleHistoryItem As ScheduleHistoryItem)
            MyBase.New()
            Me.ScheduleHistoryItem = objScheduleHistoryItem
        End Sub


        Public Overrides Sub DoWork()
            Try
                ' perform some tasks
                Dim strPath As String = HttpRuntime.AppDomainAppPath + "VB_DID_IT.TXT"
                Using sw As StreamWriter = New StreamWriter(strPath, True)
                    sw.WriteLine(DateTime.Now.ToString() + " - VB DID IT!")
                    sw.Close()
                End Using

                ' report success to the scheduler framework
                ScheduleHistoryItem.Succeeded = True

            Catch ex As Exception

                ScheduleHistoryItem.Succeeded = False
                ScheduleHistoryItem.AddLogNote("EXCEPTION" + ex.ToString())
                Errored(ex)
                Exceptions.LogException(ex)

            End Try
        End Sub
    End Class

End Namespace

 

At the risk of appearing overly verbose, I want to present the topic of DotNetNuke permissions by working from the database level all the way up through the data providers, controllers, and info classes. One of the benefits of using layers of abstraction is that it allows programmers like you and me to work with frameworks without having to know what the cogs inside are doing. However, I find that if you're going to be using the classes in the DotNetNuke framework, then you really must know what is going on behind the scenes. Since there is very little documentation at this time, the only way to learn how to work with the framework is to dig deep. So let's do it.

The Common Patterns

If you haven't picked up on it by now, just about everything within the DotNetNuke framework follows the same few patterns. The tables in the database have associated stored procedures (sprocs) that are used for CRUD (create, read, update, delete) operations. Inside the data provider classes there are methods for CRUD operations that call the sprocs in the database. For each genera of framework functionality there exists controller classes that interact with the data provider in order to provide strongly-typed classes that encapsulate the information from the database. In one way or another, the controller classes have the data from the database "hydrated" into the info classes that you end up accessing in your code (i.e. UserInfo, PermissionInfo, ModuleInfo...). The controller also "un-hydrates" the data from the info classes when in order to send the information back to the data provider. This is how the permissions system works in the DotNetNuke framework.

In The Database

For this portion of the article I will assume that you are using MSSQL and the SqlDataProvider. For those of you who are using some other database, you are freaks! Get with the show! But really, your job will be so much less painful if you use MSSQL with .Net. I say this coming from several years of developing .Net against FoxPro, so trust me on this. But I digress.

The DNN database contains several tables that are pertinent to our discussion. First we have the Permission table. This table holds the different types of permission that you can give to a user or a role. For example "View", "Edit", and "Full Access" are three of the permissions in this table. The PermissionCode column is a string of text that identifies what part of the system or what module the permission is used by. The built-in permissions for the framework begin with the text "SYSTEM_". If your code needs to give custom permissions to its entities, then you could use this table in order to define those permissions. You just need to create your own PermissionCode string as well as set the ModuleDefID column to the ID of the definition for your module. My guess is that there are facilities in the DNN module definition system to register this information for you. Also in the Permission table there is a PermissionKey table. This column, along with PermissionCode in essence establish a compound key for the record to be referenced with.

The other tables that we are interested in are the FolderPermission, ModulePermission, and TabPermission tables. These guys hold granular permission settings for you, you guessed it, folders, module instances, and pages, respectively. Records in these tables will contain the ID of a user or a role, along with the ID of a folder, module instance, or a tab. Records in these tables also have a foreign key to the Permission table so to specify what type of permission you are giving the user or role.

Sprocs

As I stated before, the DotNetNuke framework utilizes stored procedures in its SqlDataProvider to do all its CRUD operations in the database. There are too many to list here, but suffice it to say there is a stored procedure for just about every method defined in the DotNetNuke abstract data provider class (DotNetNuke.Data.DataProvider). Inside the SqlDataProvider, the SqlHelper class is used to execute these sprocs. So if you ever wonder where the SQL code is, the sprocs are where you need to look.

The Info Classes

In order to provide strongly-typed entities to work with, the DNN framework has classes that hold the information from a single record in the database. The FolderPermission, ModulePermission, and TabPermission classes in the DotNetNuke.Security.Permissions namespace are used to encapsulate records from the FolderPermission, ModulePermission, and TabPermission tables, respectively. These classes typically have a public property for each column of their respective table in the database.

The Controller Classes

When you work with permission, you don't have to interact with the data provider directly. You instead use an instance of a controller class. The FolderPermissionController, ModulePermissionController, and TabPermissionController classes in the DotNetNuke.Security.Permissions namespace contain methods that will perform most of the common CRUD operations on the FolderPermission, ModulePermission, and TabPermission tables. When you need to retrieve information from one of the tables in the database you call a Get method on one of these controller, and you will receive an Info object, or a collection of Info objects back. When you want to create or update records in one of the tables in the database you call an Add or Update method on one of these controllers, and pass it a populated instance of one the associated Info classes. This pattern keeps your code clean, and abstracts all of the data access to its own layer.

On To The Good Stuff - Examples

Here is an example of how you can add a new ModulePermission for either a user or a role.

ModulePermissionExample1CS.ascx:

<%@ Control Language="C#" AutoEventWireup="true" CodeFile="ModulePermissionExample1CS.ascx.cs"
    Inherits="ModulePermissionExample1CS" %>
<asp:DropDownList ID="ModulesDropDownList" runat="server" Width="300px">
</asp:DropDownList>
<br />
<asp:DropDownList ID="UsersDropDownList" runat="server" Width="300px">
</asp:DropDownList>
<br />
<asp:DropDownList ID="RolesDropDownList" runat="server" Width="300px">
</asp:DropDownList>
<br />
<asp:DropDownList ID="PermissionsDropDownList" runat="server" Width="300px">
</asp:DropDownList>
<br />
<asp:Button ID="Button1" runat="server" Text="Add Permission" 
    onclick="Button1_Click" />

 

ModulePermissionExample1CS.ascx.cs:

using System;

using DotNetNuke.Entities.Modules;
using DotNetNuke.Security.Permissions;
using DotNetNuke.Entities.Users;
using DotNetNuke.Security.Roles;

public partial class ModulePermissionExample1CS : PortalModuleBase
{
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            // populate Modules drop-down lists
            ModuleController moduleController 
                = new ModuleController();
            // get ArrayList of ModuleInfo
            ModulesDropDownList.DataSource 
                = moduleController.GetAllTabsModules(PortalId, false);
            ModulesDropDownList.DataTextField = "ModuleTitle";
            ModulesDropDownList.DataValueField = "ModuleID";
            ModulesDropDownList.DataBind();
            ModulesDropDownList.Items.Insert(0, "- Select Module Instance -");

            // populate Users drop-down lists
            // get ArrayList of UserInfo
            UsersDropDownList.DataSource 
                = UserController.GetUsers(PortalId, false);
            UsersDropDownList.DataTextField = "Username";
            UsersDropDownList.DataValueField = "UserID";
            UsersDropDownList.DataBind();
            UsersDropDownList.Items.Insert(0, "- Select User -");

            // populate Roles drop-down list
            RoleController roleController 
                = new RoleController();
            // get ArrayList of RoleInfo
            RolesDropDownList.DataSource = roleController.GetRoles();
            RolesDropDownList.DataTextField = "RoleName";
            RolesDropDownList.DataValueField = "RoleID";
            RolesDropDownList.DataBind();
            RolesDropDownList.Items.Insert(0, "- Select Role -");

            // populate Permissions drop-down list
            PermissionController permissionController 
                = new PermissionController();
            PermissionsDropDownList.DataSource 
                = permissionController.GetPermissionByCodeAndKey("SYSTEM_MODULE_DEFINITION", "");
            PermissionsDropDownList.DataTextField = "PermissionName";
            PermissionsDropDownList.DataValueField = "PermissionID";
            PermissionsDropDownList.DataBind();
            PermissionsDropDownList.Items.Insert(0, "- Select Permission -");
        }
    }
    protected void Button1_Click(object sender, EventArgs e)
    {
        // create instance of ModulePermissionController
        // to do all the work for us
        ModulePermissionController modPermissionController
            = new ModulePermissionController();

        // create instance of ModulePermissionInfo to
        // hold our permission information
        ModulePermissionInfo modPermInfo
            = new ModulePermissionInfo();

        // i'm not sure if AllowAccess is actually used
        // but the framework seems to set this to true
        // when it adds ModuleInfo instances to the DB
        modPermInfo.AllowAccess = true;

        // set id of the permission that was selected
        modPermInfo.PermissionID
            = int.Parse(PermissionsDropDownList.SelectedValue);

        // set id of the Module, not the TabModule
        // ... i know, this is confusing
        modPermInfo.ModuleID = int.Parse(ModulesDropDownList.SelectedValue);

        if (UsersDropDownList.SelectedIndex > 0)
        {
            // set id of user
            modPermInfo.UserID = int.Parse(UsersDropDownList.SelectedValue);

            // save the ModulePermissionInfo to the db
            modPermissionController.AddModulePermission(modPermInfo);
        }
        else if (RolesDropDownList.SelectedIndex > 0)
        {
            // set id of role
            modPermInfo.RoleID = int.Parse(RolesDropDownList.SelectedValue);

            // save the ModulePermissionInfo to the db
            modPermissionController.AddModulePermission(modPermInfo);
        }

        // set drop-down indexes back to 0
        ModulesDropDownList.SelectedIndex = 0;
        UsersDropDownList.SelectedIndex = 0;
        RolesDropDownList.SelectedIndex = 0;
        PermissionsDropDownList.SelectedIndex = 0;
    }
}

 

It looks like Paterra and Walker's new Wrox book on DotNetNuke got pushed back 6 months. I can only assume that they're holding off until the first few Cambrian features are released.

Yo Chris, what's up with that?

I knew that when Barnes & Noble promised me a ship date of February 26th, they were lying through their teeth. So much for my pre-order.

If you have been writing your own DotNetNuke modules for any amount of time you have probably run across the concept of Inter-Module Communication (IMC for short). IMC particularly refers to the facility that the DotNetNuke framework provides to allow data to be passed between modules during the ASP.Net page life cycle. It is a very elegant solution for passing data between modules, on the server side. That said, it tends to be a topic that most first-time IMC users have trouble getting right. The documentation is thin, and there aren’t too many examples out there. So in this article we will be tackling this subject as well as taking a look at a few examples.

In my follow-up post I want to discuss alternative ways for modules to communicate. There are numerous ways to pass data between modules, both on the server and on the client side.We will see how to use JavaScript, DNN Event Messaging, Bubbling Events, Control Nesting, the DNN Client API, ASP Sessions, and the Query String, all to communicate between modules.

That is a lot to cover, so let’s get started!

DotNetNuke Inter-Module Communication (IMC)

As general as the term “Inter-Module Communication” may sound, when DNN module developers use the term IMC they are specifically referring to the facility within the DNN framework which allows you to use VB.Net or C# in your code-behind in order to pass information between modules. IMC communication always occurs somewhere within the ASP.Net page life cycle. Therefore this method of passing data between modules requires exactly one execution of the page life cycle. If user interaction is required to trigger the communication, then a post-back will need to occur from the browser to trigger the page life cycle in which the IMC will take place.

How IMC Works

IMC functionality is divided into two parts: Communicating (sending messages) and Listening (receiving messages). In order to add one or both of these functionalities to a modules, you must have the module class implement the respective IMC interfaces which the DNN Framework has defined for you.

The IModuleCommunicator and IModuleListener Interfaces

Both IModuleCommunicator and IModuleListener live inside the DotNetNuke.Entities.Modules.Communications namespace, so you may find it convenient to reference these namespaces at the top of your code. You will find that I have included such references throughout my examples.

Implementing the IModuleCommunicator interface allows your module to dispatch messages to all the modules on the page that are listening for IMC messages. On the flip side of the equation, implementing the IModuleListener interface allows your module to receive all the messages sent from modules on the same page.

Implementing IModuleCommunicator

To implement IModuleCommunicator all you need to do is define an instance of the ModuleCommunicationEvenHandler delegate as an event named ModuleCommunication within your class.

 

C# Example:

using System;
using DotNetNuke.Entities.Modules;
using DotNetNuke.Entities.Modules.Communications;

public partial class SampleCommunicatorCS : 
    PortalModuleBase, IModuleCommunicator
{
    public event ModuleCommunicationEventHandler ModuleCommunication;
}

VB Example:

Imports DotNetNuke.Entities.Modules
Imports DotNetNuke.Entities.Modules.Communications

Partial Class SampleCommunicatorVB
    Inherits PortalModuleBase
    Implements IModuleCommunicator

    Public Event ModuleCommunication(ByVal sender As Object, _
        ByVal e As ModuleCommunicationEventArgs) _
        Implements IModuleCommunicator.ModuleCommunication
End Class

 

When you implement the IModuleCommunicator interface, the DotNetNuke framework knows that your module has an instance of the ModuleCommunicationEventHandler delegate type named ModuleCommunication. This guarantees that the DotNetNuke core can wire-up to your module so that it can be notified when you raise an IMC communication event. Somewhere deep in the DNN core code, there is likely a line that adds an event handler to your delegate if your module implements IModuleCommunicator:

 

if (yourModuleInstance is IModuleCommunicator)
{
    yourModuleInstance.ModuleCommunication +=
        new ModuleCommunicationEventHandler(notifyIMCListeners);
}

 

By doing this, the DNN core can respond to every ModuleCommunication event that your module raises.

You Raise a ModuleCommunication Event to Send an IMC Message

Sending an IMC message is as simple as raising your module's ModuleCommunication event. In C# you simply call ModuleCommunication(...), passing to it a reference to your module, and an instance of ModuleCommunicationEventArgs.

 

ModuleCommunication(this, mcArgs);

 

To raise the ModuleCommunication event in VB.Net, you use the RaiseEvent keyword in addition to calling ModuleCommunication(...), passing to it a reference to your module, and an instance of ModuleCommunicationEventArgs.

 

RaiseEvent ModuleCommunication(Me, mcArgs)

 

Creating the Event Args Object

When you raise the ModuleCommunication event you are required to pass in an instance of the ModuleCommunicationEventArgsDotNetNuke class which is found in the Entities.Modules.Communications namespace. This class is like an envelope that you use to send IMC messages out to other modules. You need to instantiate a copy and set its properties before you pass it to the ModuleCommunication call.

 

C# Example:

// the IMC message data gets stored inside
// a ModuleCommunicationEventArgs object
ModuleCommunicationEventArgs mcArgs = 
    new ModuleCommunicationEventArgs();
mcArgs.Sender = "SampleCommunicatorModule";
mcArgs.Target = "Arbitrary Text";
mcArgs.Text = "Your payload text";
mcArgs.Type = "Your custom type";
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load("path/to/xml/doc.xml");
mcArgs.Value = xmlDoc;

// if ModuleCommunication is null,
// the cache settings for your module
// might need to be set to 0 (turned off)
if (ModuleCommunication != null)
{
    // calling your ModuleCommunication delegate event
    // will cause the event to be raised
    ModuleCommunication(this, mcArgs);
}

VB Example:

Dim mcArgs As ModuleCommunicationEventArgs _
    = New ModuleCommunicationEventArgs()

mcArgs.Sender = "SampleCommunicatorModule - VB"
mcArgs.Target = "Arbitrary Text"
mcArgs.Text = "Your Payload Text"
mcArgs.Type = "Your Custom Type"
Dim xmlDoc As System.Xml.XmlDocument = New System.Xml.XmlDocument()
xmlDoc.Load("path/to/xml/doc.xml")
mcArgs.Value = xmlDoc

RaiseEvent ModuleCommunication(Me, mcArgs)

 

All of the ModuleCommunicationEventArgs properties can be used any way you see fit. It is important to remember that every IMC message that your module sends out will be received by every module that implements IModuleListener. There are no built-in mechanisms for directing IMC messages to particular modules. Therefore, it is up to the module developer to establish their own consistent use of the ModuleCommunicationEventArgs properties in order to identify the messages that a particular module needs to listen for.

 The Sender, Target, Text, and Type properties of the ModuleCommunicationEventArgs class are of type String. The Value property is of type object. In the example above I set the Value property to an instance of XmlDocument just to demonstrate that you can pass types other than String in this property.

Implementing IModuleListener

In order for your module to receive IMC messages it must implement the IModuleListener interface. This interface requires your class to have a method named OnModuleCommunication which takes two parameters, and Object type and a ModuleCommunicationEventArgs type, respectively.

C# Example:

using System;
using DotNetNuke.Entities.Modules;
using DotNetNuke.Entities.Modules.Communications;

public partial class SampleListenerCS : 
    PortalModuleBase, IModuleListener
{
    public void OnModuleCommunication(object s, 
        ModuleCommunicationEventArgs e)
    {
        throw new NotImplementedException();
    }
}

VB Example:

Imports DotNetNuke.Entities.Modules
Imports DotNetNuke.Entities.Modules.Communications

Partial Class SampleListenerVB
    Inherits PortalModuleBase
    Implements IModuleListener

    Sub OnModuleCommunication(ByVal s As Object, _
                              ByVal e As ModuleCommunicationEventArgs) _
        Implements IModuleListener.OnModuleCommunication

    End Sub
End Class

 

Your OnModuleCommunication Method Executes Every Time Any Module Raises a ModuleCommunication Event

When you implement the IModuleListener interface the DotNetNuke framework automatically adds your OnModuleCommunication method to the delegate that responds to all the ModuleCommunication events. When your OnModuleCommunication method is executed, it is passed a reference to the module that raised the ModuleCommunication even, this is the first argument of your OnModuleCommunication method. Your method also gets passed the ModuleCommunicationEventArgs object that contains all the information that was defined by the object that raised the event.

ModuleCommunicationEventArgs is Key

The ModuleCommunicationEventArgs objec is the key piece of the IMC framework because it is the single channel in which the communication happens. Anything placed into the ModuleCommunicationEventArgs object from inside the module that raises the ModuleCommunication event gets passed along to every OnModuleCommunication method of the modules that implement the IModuleListener interface.

 

Full Examples For Your Dissecting Pleasures - C#

Communicator XML File in C# - SampleCommunicatorCS.ascx:

<%@ Control Language="C#" AutoEventWireup="true" 
    CodeFile="SampleCommunicatorCS.ascx.cs" 
    Inherits="SampleCommunicatorCS" %>

 

Communicator Code-Behind File in C# - SampleCommunicatorCS.ascx.cs:

using System;
using DotNetNuke.Entities.Modules;
using DotNetNuke.Entities.Modules.Communications;
using System.Xml;

public partial class SampleCommunicatorCS : 
    PortalModuleBase, IModuleCommunicator
{
    public event ModuleCommunicationEventHandler ModuleCommunication;

    protected void Page_Load(object sender, EventArgs e)
    {
        // the IMC message data gets stored inside
        // a ModuleCommunicationEventArgs object
        ModuleCommunicationEventArgs mcArgs = 
            new ModuleCommunicationEventArgs();
        mcArgs.Sender = "SampleCommunicatorModule";
        mcArgs.Target = "Arbitrary Text";
        mcArgs.Text = "Your payload text";
        mcArgs.Type = "Your custom type";
        XmlDocument xmlDoc = new XmlDocument();
        xmlDoc.Load("path/to/xml/doc.xml");
        mcArgs.Value = xmlDoc;

        // if ModuleCommunication is null,
        // the cache settings for your module
        // might need to be set to 0 (turned off)
        if (ModuleCommunication != null)
        {
            // calling your ModuleCommunication delegate event
            // will cause the event to be raised
            ModuleCommunication(this, mcArgs);
        }
    }
}

Listener XML File in C# - SampleListenerCS.ascx:

<%@ Control Language="C#" AutoEventWireup="true" 
    CodeFile="SampleListenerCS.ascx.cs"
    Inherits="SampleListenerCS" %>
<asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>

 

Listener Code-Behind in C# - SampleListenerCS.ascx.cs:

using System;
using DotNetNuke.Entities.Modules;
using DotNetNuke.Entities.Modules.Communications;

public partial class SampleListenerCS : 
    PortalModuleBase, IModuleListener
{
    public void OnModuleCommunication(object s, 
        ModuleCommunicationEventArgs e)
    {
        if (e.Target == "Pizza Inspectors")
        {
            if(e.Text.ToLower().Contains("garlic")
            {
                Label1.Text = "Garlic Detected In Pizza";
            }
        }
    }
}

 

Full Examples For Your Dissecting Pleasures - VB

 

Communicator XML File in VB - SampleCommunicatorVB.ascx

<%@ Control Language="VB" AutoEventWireup="true"
     CodeFile="SampleCommunicatorVB.ascx.vb" 
     Inherits="SampleCommunicatorVB" %>

Communicator Code-Behind in VB - SampleCommunicatorVB.ascx.vb

Imports DotNetNuke.Entities.Modules
Imports DotNetNuke.Entities.Modules.Communications

Partial Class SampleCommunicatorVB
    Inherits PortalModuleBase
    Implements IModuleCommunicator

    Public Event ModuleCommunication(ByVal sender As Object, _
        ByVal e As ModuleCommunicationEventArgs) _
        Implements IModuleCommunicator.ModuleCommunication

    Protected Sub Page_Load(ByVal sender As Object, _
                            ByVal e As System.EventArgs) Handles Me.Load

        Dim mcArgs As ModuleCommunicationEventArgs _
            = New ModuleCommunicationEventArgs()

        mcArgs.Sender = "SampleCommunicatorModule - VB"
        mcArgs.Target = "Arbitrary Text"
        mcArgs.Text = "Your Payload Text"
        mcArgs.Type = "Your Custom Type"
        Dim xmlDoc As System.Xml.XmlDocument = New System.Xml.XmlDocument()
        xmlDoc.Load("path/to/xml/doc.xml")
        mcArgs.Value = xmlDoc

        RaiseEvent ModuleCommunication(Me, mcArgs)

    End Sub
End Class

 

Listener XML File in VB - SampleListenerVB.ascx

<%@ Control Language="VB" AutoEventWireup="false" 
    CodeFile="SampleListenerVB.ascx.vb" 
    Inherits="SampleListenerVB" %>
<asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>

 

Listener Code-Behind in VB - SampleListenerVB.ascx.vb

Imports DotNetNuke.Entities.Modules
Imports DotNetNuke.Entities.Modules.Communications

Partial Class SampleListenerVB
    Inherits PortalModuleBase
    Implements IModuleListener

    Sub OnModuleCommunication(ByVal s As Object, _
                              ByVal e As ModuleCommunicationEventArgs) _
        Implements IModuleListener.OnModuleCommunication
        If e.Target = "Pizza Inspectors" Then
            If e.Text.ToLower.Contains("garlic") Then
                Label1.Text = "Garlic Detected In Pizza!"
            End If
        End If

    End Sub
End Class

Who Is Rafe

rafe

I Am Rafe Kemmis

I am an audacious entrepreneur with a double bachelor of science in Computer Science and Mathematics. I specialize in Microsoft ASP.Net web application development as well as accredited information systems auditing.

Questions?

Always a thoughtful response. You may post your question on an article, or contact me directly.

Hire Me.

I provide custom solutions to complex problems. I can help your business no matter how large or small.

Contact me now.

Subscribe