Introduction
Welcome, keen DNN module developer, to this walkthrough. Here I'll describe, step by step, how to add custom permissions to a DNN module. This tutorial may change in future, as new information emerges; to keep informed of new developments, subscribe to the RSS feed of my blog.
First things first. Here are the assumptions I'm making in this walkthrough:
- You know about basic module structure, the DAL, and the module starter kit.
- You're using Visual Studio or Visual Studio Express with the module templates installed.
- You know about C# and ASP.NET.
These are, of course, not obligatory. Your life will be a lot easier if these assumptions apply, though.
Are you sitting comfortably? Then, we'll begin...
Step 1: Creating a Dummy Module
The easy bit. You should have done this before, so I'll keep it short. Go to your DNN project in Visual Web Developer, right-click on the top node, and select "Add New Item". Select "DotNetNuke Dynamic Module" and the language of your choice (this walkthrough will use C#). Enter 'PermissionTest' as the module name. Rename the folders as per instructions, don't forget to amend the web.config file, and hit Ctrl-F5 to start your development installation of DNN. Go to Host->Module Definitions, scroll to the bottom of the page, click on "Import Module Definition", select the manifest of the module you just created, and hit "Import Manifest".
Your module should appear in the list of installed modules, but there's a little bug to take care of. Click on the pencil icon next to 'PermissionTest' to bring up the module properties page. If you have some sort of prefix in the module name and folder name text boxes (CreativeCats.PermissionTest, in my case), delete the prefix so that only 'PermissionTest' remains. Next, go to Host->SQL, copy and paste the contents of the file "01.00.00.SqlDataProvider" which was created for you in the DesktopModules/MODULENAME folder. Make sure the "Run as Script" checkbox is ticked, and click "Execute".
If I just lost you, you may want to go back to Michael Washington's tutorial series, which gives detailed instructions on how to create a simple module.
Now, go to any page on your site and install the module, just to see that the code so far is OK. Click on "Settings" to take a look at the default settings page. You'll see the standard permissions that apply to all modules, "View Module" and "Edit Module". That's what we're going to change.
According to Vicenç Masanas' blog entry, there is no automated way of creating custom module permissions during module installation, so he suggests using the IUpgradeable
interface to accomplish this. Yes, the blog is from 2006, but so far, I haven't found any information to the contrary, so that's the method I'm using. (I have seen a suggestion for creating the permissions during the page_load
event, but I can't help wondering if that is truly wise... However, if you know anything I don't, please give me a shout.)
Purely for development purposes, we're going to cheat by putting a button on our view control and wiring the button_click
event handler to add the permissions. That's just to give you the opportunity to step through what's happening in the debugger and to allow testing without continually uninstalling and reinstalling the module. So, add a button at the bottom of your ViewPermissionTest.ascx (or whatever yours is called), double-click it to add the event handler in the code-behind file, and add the following lines:
PermissionTestController cntrl = new PermissionTestController();
cntrl.UpgradeModule("01.00.00");
Step 2: Implementing the IUpgradeable Interface
First, of course, we need to declare our controller class as implementing the IUpgradeable
interface. The class declaration should look something like this:
public class PermissionTestController : ISearchable, IPortable, IUpgradeable
To implement the IUpgradeable
interface, we need to implement the following method in the module's controller class:
string UpgradeModule(string Version)
(The controller class has been created for you in the App_Code directory. I called my module PermissionTest, so in my case, the controller is 'Visual Studio 2008\WebSites\DNN484\App_Code\PermissionTestController.cs'.)
The standard way of doing this seems to be by checking the module version and calling whatever you want to happen during the upgrade. In our case, creating the custom permissions via a private method, InitModulePermissions()
. First off, you'll need to include the following namespaces in the controller class:
using DotNetNuke.Security.Permissions;
using DotNetNuke.Entities.Modules;
using DotNetNuke.Entities.Modules.Definitions
using System.Collections;
Next, you need to implement the IUpgradeable
functionality:
public string UpgradeModule(string Version)
{
if (Version == "01.00.00")
{
InitModulePermissions();
}
return Version;
}
OK, explanations about the InitModulePermissions
method:
In order to add permissions, we need a PermissionController
.
private void InitModulePermissions()
{
PermissionController permCtl = new PermissionController();
}
To add a permission, we need to call the PermissionController
's AddPermission
method, which takes a PermissionInfo
object as an argument. So now, we have:
private void InitModulePermissions()
{
PermissionController permCtl = new PermissionController();
try
{
PermissionInfo pi = new PermissionInfo();
permCtl.AddPermission(pi);
}
catch{}
}
Of course, an empty PermissionInfo
object is no use to anybody. At this point, it's worth taking a little detour to look at the Permission table in DNN:
This is the information we need to provide. The Permission Code identifies which module settings will be affected. This code can also be used to retrieve permissions later (you'll see...). The Permission Key is, well, a key value, and the Permission Name is what will be displayed on the settings page. These three values are defined by you, like so:
private void InitModulePermissions()
{
PermissionController permCtl = new PermissionController();
try
{
PermissionInfo pi = new PermissionInfo();
pi.PermissionCode = "PERMISSIONTEST";
pi.PermissionKey = "PERMISSION1";
pi.PermissionName = "A custom permission";
permCtl.AddPermission(pi);
}
catch{}
}
The ModuleDefID
is a little trickier. Each module's ModuleDefID
is stored in a ModuleDefinitionInfo
object. This can be retrieved through the GetModuleDefinitionByName()
method of the ModuleDefinitionController
. However...
GetModuleDefinitionByName()
needs, apart from the module name, the DesktopModuleID
, which is stored in a DesktopModuleInfo
object. And, that can be accessed through yet another controller, the DesktopModuleController
. Which leaves us with the following code:
private void InitModulePermissions()
{
PermissionController permCtl = new PermissionController();
DesktopModuleController desktopMod = new DesktopModuleController();
DesktopModuleInfo desktopInfo =
desktopMod.GetDesktopModuleByModuleName("PermissionTest");
ModuleDefinitionController modDef = new ModuleDefinitionController();
ModuleDefinitionInfo modDefInfo =
modDef.GetModuleDefinitionByName(desktopInfo.DesktopModuleID,
"PermissionTest");
try
{
PermissionInfo pi = new PermissionInfo();
pi.ModuleDefID = modDefInfo.ModuleDefID;
pi.PermissionCode = "PERMISSIONTEST";
pi.PermissionKey = "PERMISSION1";
pi.PermissionName = "A custom permission";
permCtl.AddPermission(pi);
}
catch{}
}
Phew.
Note: GetModuleDefinitionByName()
takes two arguments: the Desktop Module ID and the Friendly Name of the view control in question. So, if your module has a friendly name that differs from the module name, or if you are developing a module with multiple definitions, make sure that you use the appropriate name...
But, that's it (for now). Set a breakpoint at the beginning of this new method, run up your DNN site in debug mode, click on the button on your new module, and see what your handiwork does. If you look at the Permission table when this method has finished, you should see your new entry, and looking at the settings for your module, you should have a new column of checkboxes...
Step 3: Encapsulating the Permissions
The code so far uses a number of magic strings - generally considered as practical as a chocolate wok. A much better solution is to encapsulate the security functionality in a special class. So, in the App_Code directory, create a new class, ModuleSecurity
, with the strings you need declared as constants:
public class ModuleSecurity
{
public const string PERMISSIONCODE = "PERMISSIONTEST";
public const string PERMISSION1 = "PERMISSION1";
}
Now, you can change your try()
block to use these constants:
try
{
PermissionInfo pi = new PermissionInfo();
pi.ModuleDefID = modDefInfo.ModuleDefID;
pi.PermissionCode = ModuleSecurity.PERMISSIONCODE;
pi.PermissionKey = ModuleSecurity.PERMISSION1;
pi.PermissionName = "A custom permission";
permCtl.AddPermission(pi);
}
Step 4: Implementing Permission-based Functionality
OK, putting the custom permissions into the module settings page is all very well, but without a way of actually checking the permissions, for whatever nefarious purpose you have in mind, it's a moot point. So, let's add a little extra functionality to our module - a secret message (woooo...). Just add a Label
to your view control, set some text, and change the Visibility
to False
.
<asp:label id=Label1 Visible="False" Text="My Secret Message" runat="server"></asp:label>
Next, we need to modify the ModuleSecurity
class to give us a way of retrieving the permissions. We can get the permissions for each individual module through a ModulePermissionController
, but...
The ModulePermissionController
's HasModulePermission()
method needs a ModulePermissionCollection
, which can be retrieved through the ModuleInfo
class, which each module inherits, similar to its ModuleID
. So, if we want to check a module's permissions, we need to pass its ModuleInfo
into the constructor of our ModuleSecurity
class. We also need a private member for each permission, and a way of retrieving it. That makes the complete ModuleSecurity
class:
public class ModuleSecurity
{
public const string PERMISSIONCODE = "PERMISSIONTEST";
public const string PERMISSION1 = "PERMISSION1";
private bool _permission1;
public ModuleSecurity(ModuleInfo moduleInfo)
{
ModulePermissionCollection permCollection = moduleInfo.ModulePermissions;
_permission1 =
ModulePermissionController.HasModulePermission(permCollection, PERMISSION1);
}
public bool HasPermission1
{
get { return _permission1; }
}
}
Say, we want to display the new Label
based on permissions. That means adding the following lines to the Page_Load
event of the view control:
ModuleSecurity ms = new ModuleSecurity(this.ModuleConfiguration);
Label1.Visible = ms.HasPermission1;
And, that's all there is to it. Add a few test modules to a page, give different permissions to different user groups, and see how the label visibility changes based on each module's permissions...
Epilogue: The Belt Upon the Braces
It you take a look at some of the standard DNN modules that use custom permissions (such as the User Defined Table), you'll see that existing permissions are retrieved and checked before adding new ones. Now, when I tried adding redundant permissions by clicking the button several times, nothing untoward happened, i.e., the permissions table did not suddenly sprout multiple entries for each permission. However, if you want to be on the safe side, it's worth putting this little check in place - it's not as if it's hugely complicated. I'll give you the entire source code; the change is straightforward, and I'm sure you'll figure it out...
private void InitModulePermissions()
{
PermissionController permCtl = new PermissionController();
DesktopModuleController desktopMod = new DesktopModuleController();
DesktopModuleInfo desktopInfo =
desktopMod.GetDesktopModuleByModuleName("PermissionTest");
ModuleDefinitionController modDef = new ModuleDefinitionController();
ModuleDefinitionInfo modDefInfo =
modDef.GetModuleDefinitionByName(desktopInfo.DesktopModuleID,
"PermissionTest");
ArrayList arr =
permCtl.GetPermissionByCodeAndKey(ModuleSecurity.PERMISSIONCODE, "");
bool bPermission1=false;
foreach (PermissionInfo p in arr)
{
if ((p.PermissionKey == ModuleSecurity.PERMISSION1)&&
(p.ModuleDefID == modDefInfo.ModuleDefID))
bPermission1 = true;
}
try
{
PermissionInfo pi = new PermissionInfo();
pi.ModuleDefID = modDefInfo.ModuleDefID;
if(!bPermission1)
{
pi.PermissionCode = ModuleSecurity.PERMISSIONCODE;
pi.PermissionKey = ModuleSecurity.PERMISSION1;
pi.PermissionName = "A custom permission";
permCtl.AddPermission(pi);
}
}
catch{}
}