New Community Website

Ordinarily, you'd be at the right spot, but we've recently launched a brand new community website... For the community, by the community.

Yay... Take Me to the Community!

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 3, 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)
Anas Ghanem
Just wanted to mention that when working with latest DNN version 9, above private method (GetIndexActions) should be set as public in order to avoid this error:
The expected method to get the module actions cannot be found. Type: Dnn.Modules.TaskList.Controllers.ItemController, Method: GetIndexActions
Anas Ghanem Monday, February 20, 2017 11:09 AM (link)

Comment Form

Only registered users may post comments.


Aderson Oliveira (22)
Alec Whittington (11)
Alessandra Davies (3)
Alex Shirley (10)
Andrew Hoefling (3)
Andrew Nurse (30)
Andy Tryba (1)
Anthony Glenwright (5)
Antonio Chagoury (28)
Ash Prasad (37)
Ben Schmidt (1)
Benjamin Hermann (25)
Benoit Sarton (9)
Beth Firebaugh (12)
Bill Walker (36)
Bob Kruger (5)
Bogdan Litescu (1)
Brian Dukes (2)
Brice Snow (1)
Bruce Chapman (20)
Bryan Andrews (1)
cathal connolly (55)
Charles Nurse (163)
Chris Hammond (213)
Chris Paterra (55)
Clint Patterson (108)
Cuong Dang (21)
Daniel Bartholomew (2)
Daniel Mettler (181)
Daniel Valadas (48)
Dave Buckner (2)
David Poindexter (12)
David Rodriguez (3)
Dennis Shiao (1)
Doug Howell (11)
Erik van Ballegoij (30)
Ernst Peter Tamminga (80)
Francisco Perez Andres (17)
Geoff Barlow (12)
George Alatrash (12)
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 (274)
John Mitchell (1)
Jon Henning (14)
Jonathan Sheely (4)
Jordan Coopersmith (1)
Joseph Craig (2)
Kan Ma (1)
Keivan Beigi (3)
Kelly Ford (4)
Ken Grierson (10)
Kevin Schreiner (6)
Leigh Pointer (31)
Lorraine Young (60)
Malik Khan (1)
Matt Rutledge (2)
Matthias Schlomann (16)
Mauricio Márquez (5)
Michael Doxsey (7)
Michael Tobisch (3)
Michael Washington (202)
Miguel Gatmaytan (3)
Mike Horton (19)
Mitchel Sellers (40)
Nathan Rover (3)
Navin V Nagiah (14)
Néstor Sánchez (31)
Nik Kalyani (14)
Oliver Hine (1)
Patricio F. Salinas (1)
Patrick Ryan (1)
Peter Donker (54)
Philip Beadle (135)
Philipp Becker (4)
Richard Dumas (22)
Robert J Collins (5)
Roger Selwyn (8)
Ruben Lopez (1)
Ryan Martinez (1)
Sacha Trauwaen (1)
Salar Golestanian (4)
Sanjay Mehrotra (9)
Scott McCulloch (1)
Scott Schlesier (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)
Steven Fisher (1)
Tony Henrich (3)
Torsten Weggen (3)
Tycho de Waard (4)
Vicenç Masanas (27)
Vincent Nguyen (3)
Vitaly Kozadayev (6)
Will Morgenweck (40)
Will Strohl (180)
William Severance (5)
What is Liquid Content?
Find Out
What is Liquid Content?
Find Out
What is Liquid Content?
Find Out