Learn More





DNN Community Blog

The Community Blog is a personal opinion of community members and by no means the official standpoint of DNN Corp or DNN Platform. This is a place to express personal thoughts about DNNPlatform, the community and its ecosystem. Do you have useful information that you would like to share with the DNN Community in a featured article or blog? If so, please contact .

The use of the Community Blog is covered by our Community Blog Guidelines - please read before commenting or posting.

Module Development in DNN 8: 6 - Handling Module Actions in MVC and SPA modules

DNN 8 will support creating modules using ASP.NET MVC version 5.1 (or later) as well as using a SPA (Single Page Application) module using HTML 5, JavaScript and ASP.NET Web API.  In previous blogs in this series I have described how these modules can be built and I have shown how localization is handled.  One topic still to describe is how both of these approaches can register module actions so they show on DNN’s module action menu.

ModuleAction attribute

I will start with how Module Actions can be registered for MVC modules.  In a Web Form’s code-behind file you can add Module Actions by implementing the IActionable interface.  This approach is not possible in an MVC Controller as the Controller can have many “Views” and each View would need its own set of Module Actions.  We therefore decided to add a new ModuleAction attribute implemented as an MVC Action Filter.  This attribute allows you to define a Module Action.

Listing 1: The ModuleAction Attribute

   1:  [HttpGet]
   2:  [ModuleAction(ControlKey = "Edit", TitleKey = "AddContact")]
   3:  public ActionResult Index()
   4:  {
   5:      var contacts = _repository.GetContacts(PortalSettings.PortalId);
   7:      return View(contacts.ToList());
   8:  }

Each ModuleAction attribute will add a single Module Action to the Module Actions menu.  In this case, the Module Action is the “Edit” action and its title is defined by the Localization key “AddContact”.  The ModuleAction attribute exposes the following properties:

  • ControlKey – the key used to identify the Module Control to load
  • Icon – the Url for the Icon to use
  • SecurityAccessLevel – the SecurityAccessLevel setting
  • Title – the title (text) displayed in the Actions menu
  • TitleKey – the localization key for the Title (if present this overrides the Title setting)

Each ModuleAction attribute defines a single Module Action.  For complex modules this could mean that an Action method could have a lot of ModuleAction attributes.  In addition the attribute only defines a subset of the ModuleAction properties – it is designed to provide a simple solution to cover the 80% use case.  To cover more complex situations we have also included a ModuleActionItems attribute.

ModuleActionItems attribute

The ModuleActionItems attribute is also implemented as an Action Filter.  It has two parameters:

  • Type – the type of a class (if not present this will default to the current Controller)
  • MethodName – the name of a method to call in the class defined by the Type parameter.  If not present this defaults to “Get{ActionMethos}Actions” where {ActionMethod} is the name of the Action Method where the attribute is applied.

As an example lets replace the single ModuleAction attribute in Listing 1 with the ModuleActionItems attribute.  The code changes are shown in Listing 2.

Listing 2: The ModuleActionItems attribute

   1:  [ModuleActionItems]
   2:  public ActionResult Index()
   3:  {
   4:      var contacts = _repository.GetContacts(PortalSettings.PortalId);
   6:      return View(contacts.ToList());
   7:  }
   9:  private ModuleActionCollection GetIndexActions()
  10:  {
  11:      var actions = new ModuleActionCollection();
  13:      actions.Add(new ModuleAction(-1)
  14:                  {
  15:                      CommandName = ModuleActionType.AddContent,
  16:                      CommandArgument = String.Empty,
  17:                      Icon = String.Empty,
  18:                      Title = LocalizeString("AddContact"),
  19:                      Url = ModuleContext.EditUrl("Edit"),
  20:                      Secure = SecurityAccessLevel.Edit,
  21:                      UseActionEvent = false,
  22:                      Visible = true,
  23:                      NewWindow = false
  24:                  });
  26:      return actions;
  27:  }

Essentially the ModuelActions attribute works just like IActionable, returning a collection of module actions.

Module Actions in SPA modules

One of the tenets of SPA modules is that they are built using a Single Page Application style.  This essentially means that the module shouldn’t need any Module Actions to load a secondary “page”.  However for completeness we have added a ModuleAction token.  The ModuleAction Token works like the other JSON based tokens I described in my previous blog, and works like the ModuleAction attribute exposing the same set of parameters.

  • controlKey
  • icon
  • securityAccessLevel
  • title
  • titleKey

It also adds an extra parameter so you can identify the “localResourceFile” to use for the titleKey field.  For example the token for the Edit ModuleAction used above would be:

[ModuleAction : {controlKey: “Edit”, securityAccessLevel : “Edit”,  titleKey: ”AddContent”, localResourceFile:”~/DesktopModules/Dnn/ContactList/App_LocalResources/ContactList.resx” }]

So now you can add Module Actions to your MVC or SPA modules – have fun with this new development style and let us know what you think.

For more information


Vishves Bhesania
Thanks for great articles.

When I debug this project in release mode,I got error like:

could not find ContactList_mvc.dnn.dnn
could not find ContactList_spa.dnn.dnn

Please help me further.
Vishves Bhesania Saturday, October 03, 2015 1:31 AM (link)
Danylo Kizyma
Hi Charles, thanks for this article. I have a question - what is a preferred way of using Session object with SPA modules?

Danylo Kizyma Tuesday, October 13, 2015 2:40 PM (link)
tianxi xi
HI Charles, thanks for this article,I have a question,how do package install zip. thanks.
tianxi xi Tuesday, January 19, 2016 1:52 AM (link)
Will Strohl
FYI - If you copy and paste the code snippets here, be sure to replace the quotes, as well as correct some spacing issues.

This: [ModuleAction : {controlKey: “Edit”, securityAccessLevel : “Edit”, titleKey: ”AddContent”, localResourceFile:”~/DesktopModules/Dnn/ContactList/App_LocalResources/ContactList.resx” }]

Should Be: [ModuleAction:{controlKey: "Edit", securityAccessLevel : "Edit", titleKey: "AddContent", localResourceFile: "~/DesktopModules/Dnn/ContactList/App_LocalResources/ContactList.resx" }]
Will Strohl Thursday, September 29, 2016 8:03 PM (link)

Comment Form

Only registered users may post comments.


2sic Daniel Mettler (124)
Aderson Oliveira (15)
Alec Whittington (11)
Alex Shirley (10)
Andrew Nurse (30)
Anthony Glenwright (5)
Antonio Chagoury (28)
Ash Prasad (21)
Ben Schmidt (1)
Benjamin Hermann (25)
Benoit Sarton (9)
Beth Firebaugh (12)
Bill Walker (36)
Bob Kruger (5)
Brian Dukes (2)
Brice Snow (1)
Bruce Chapman (20)
Bryan Andrews (1)
cathal connolly (55)
Charles Nurse (163)
Chris Hammond (203)
Chris Paterra (55)
Clinton Patterson (28)
Cuong Dang (21)
Daniel Bartholomew (2)
Dave Buckner (2)
David Poindexter (3)
David Rodriguez (2)
Doug Howell (11)
Erik van Ballegoij (30)
Ernst Peter Tamminga (74)
Geoff Barlow (6)
Gifford Watkins (3)
Gilles Le Pigocher (3)
Ian Robinson (7)
Israel Martinez (17)
Jan Blomquist (2)
Jan Jonas (3)
Jaspreet Bhatia (1)
Jenni Merrifield (6)
Joe Brinkman (269)
John Mitchell (1)
Jon Henning (14)
Jonathan Sheely (4)
Jordan Coopersmith (1)
Joseph Craig (2)
Kan Ma (1)
Keivan Beigi (3)
Ken Grierson (10)
Kevin Schreiner (6)
Leigh Pointer (31)
Lorraine Young (60)
Malik Khan (1)
Matthias Schlomann (15)
Mauricio Márquez (5)
Michael Doxsey (7)
Michael Tobisch (3)
Michael Washington (202)
Mike Horton (19)
Mitchel Sellers (28)
Nathan Rover (3)
Navin V Nagiah (14)
Néstor Sánchez (31)
Nik Kalyani (14)
Peter Donker (52)
Philip Beadle (135)
Philipp Becker (4)
Richard Dumas (22)
Robert J Collins (5)
Roger Selwyn (8)
Ruben Lopez (1)
Ryan Martinez (1)
Salar Golestanian (4)
Sanjay Mehrotra (9)
Scott McCulloch (1)
Scott S (11)
Scott Wilkinson (3)
Scott Willhite (97)
Sebastian Leupold (80)
Shaun Walker (237)
Shawn Mehaffie (17)
Stefan Cullmann (12)
Stefan Kamphuis (12)
Steve Fabian (31)
Timo Breumelhof (24)
Tony Henrich (3)
Torsten Weggen (2)
Vicenç Masanas (27)
Vincent Nguyen (3)
Vitaly Kozadayev (6)
Will Morgenweck (37)
Will Strohl (163)
William Severance (5)
Try Evoq
For Free
Start Free Trial
a Demo
See Evoq Live
Need More Information?