Dotnetnuke Services Framework and AjaxWebLinks


The good people at Dotnetnuke have release a new ctp which allows module developers to add external api to their modules. You can view the announcement made by Bob Kruger here and a short tutorial from Scott Schlesier here.

I created a branch of my AjaxWebLinks sample module based on asmx webservices and converted it to use the new framework. I think there are some things that can be improved but my overall experience has been good so far. I like the fact that the services work without any sort of registration. When the Dotnetnuke application starts it searches for all classes which implement the special routing interface and registers the routes to the services.

One thing that wasn’t mentioned in the tutorial (or I somehow missed it) is your request requires a “TabId” and a “ModuleId” header to check permissions successfully.

Here is a sample Ajax request from my converted module:

   $.ajax({
        type: "POST",
        beforeSend: function (xhr) {
            xhr.setRequestHeader("ModuleId", AjaxWebLinksModuleID);
            xhr.setRequestHeader("TabId", AjaxWebLinksTabID);
        },
        
        url: "/DesktopModules/AjaxWebLinks/API/Handler/GetLinks",
        data: {},
        dataType: "json",
        success: function (data) {
           

        },
        error: function () {
            
        }
    });

Download the sample module and source here

Jquery AjaxWeblinks Module for Dotnetnuke released


After a very long time I was able to finish the first version of the AjaxWeblinks module which demonstrates a method for using jQuery/Ajax functionality in a Dotnetnuke module. For now, there can only be one instance of the module on any given page. I will release another version soon which will have better scoped and enqueued javascript.

The packages are available here.

Dotnetnuke Ajax WebLinks module on Codeplex


I promised to release the complete code for the Dotnetnuke Ajax WebLinks module I have been working on. It was supposed to demonstrate a method for including jquery ajax functionality in dotnetnuke modules. However, I have not had time to finish it and write up the last two posts. In the mean time I posted the project on codeplex so people can view all the nearly complete source code. When I get some time soon I will finish this one and possibly create a version for Dotnetnuke 6.x if there isn’t already something built in that makes it unnecessary.

It can be found here

Jquery Weblinks Module Setup


Credits

In my last post I covered the conceptual design of the weblinks module. From now on I will be creating posts at different stages of coding the module. Before I move on I need to give some credit for stuff I am using to help create this. For people who need help learning how to create modules in DotNetNuke I found the tutorial by Johan Seppäläinen at sipidcode.com very useful. I am going to use the template from the DotNetNuke starter kit and much of the code in this tutorial with names change to suit the weblinks module. Although I made some changes to the original code, the iweblite project allowed me to see how this could be possible. Now that I got that out of the way let’s do some coding.

Creating Project

It really does not matter what module template is  used but I believe the module template I used to create the project is from the DotNetNuke starter kit (I have several downloaded and a custom one I created so I lost track).  The project is in C# but I will be happy to provide the visual basic version if someone provides it. I setup DotNetNuke in Microsoft Webmatrix then hit the button in webmatrix to open the project in visual studio after DotNetNuke was installed. Once in visual studio I created a new module project inside of the DesktopModules folder in my DotNetNuke installation. I also deleted a bunch of files I won’t need for this demonstration. These were any files related to editing and settings pages. We could use the settings controls in this but I want to keep this as simple as possible.

Project Screen

As you can see in the above screenshot, webmatrix has the DotNetNuke which was transferred to visual studio. Aside from our jQuery and webservice stuff this is going to be setup like a typical module so I will not go into too much detail about that stuff.

After getting my project into visual studio and removing the files I didn’t need I added the table and stored procedure creation script in the 00.00.01.SqlDataProvider (please forgive me for any typos in this one. I had to go back to make changes a few times so hope I did not forget anything).

/** Create Table **/
if not exists (select * from dbo.sysobjects where id = object_id(N'{databaseOwner}[{objectQualifier}AjaxWebLinks_WebLinks]') and OBJECTPROPERTY(id, N'IsTable') = 1)
BEGIN
CREATE TABLE {databaseOwner}[{objectQualifier}AjaxWebLinks_WebLinks]
(
[ModuleID] [int] NOT NULL,
[LinkID] [int] NOT NULL IDENTITY(1, 1),
[LinkText] [nvarchar](100) NOT NULL,
[LinkURL] [nvarchar](100) NOT NULL
)

ALTER TABLE {databaseOwner}[{objectQualifier}AjaxWebLinks_WebLinks] ADD CONSTRAINT [PK_{objectQualifier}AjaxWebLinks_WebLinks] PRIMARY KEY NONCLUSTERED ([LinkID])
CREATE CLUSTERED INDEX [IX_{objectQualifier}AjaxWebLinks_WebLinks] ON {databaseOwner}[{objectQualifier}AjaxWebLinks_WebLinks] ([ModuleID])

ALTER TABLE {databaseOwner}[{objectQualifier}AjaxWebLinks_WebLinks] WITH NOCHECK ADD CONSTRAINT [FK_{objectQualifier}AjaxWebLinks_WebLinks_{objectQualifier}Modules] FOREIGN KEY ([ModuleID]) REFERENCES {databaseOwner}[{objectQualifier}Modules] ([ModuleID]) ON DELETE CASCADE NOT FOR REPLICATION
END
GO

/** Drop Existing Stored Procedures **/

if exists (select * from dbo.sysobjects where id = object_id(N'{databaseOwner}[{objectQualifier}AjaxWebLinks_GetLinks]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
drop procedure {databaseOwner}{objectQualifier}AjaxWebLinks_GetLinks
GO

if exists (select * from dbo.sysobjects where id = object_id(N'{databaseOwner}[{objectQualifier}AjaxWebLinks_GetLink]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
drop procedure {databaseOwner}{objectQualifier}AjaxWebLinks_GetLink
GO

if exists (select * from dbo.sysobjects where id = object_id(N'{databaseOwner}[{objectQualifier}AjaxWebLinks_AddLink]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
drop procedure {databaseOwner}{objectQualifier}AjaxWebLinks_AddLink
GO

if exists (select * from dbo.sysobjects where id = object_id(N'{databaseOwner}[{objectQualifier}AjaxWebLinks_UpdateLink]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
drop procedure {databaseOwner}{objectQualifier}AjaxWebLinks_UpdateLink
GO

if exists (select * from dbo.sysobjects where id = object_id(N'{databaseOwner}[{objectQualifier}AjaxWebLinks_DeleteLink]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
drop procedure {databaseOwner}{objectQualifier}AjaxWebLinks_DeleteLink
GO

/** Create Stored Procedures **/

create procedure {databaseOwner}{objectQualifier}AjaxWebLinks_GetLinks
@ModuleId int
as

select ModuleId,
LinkID,
LinkURL,
LinkText
from {objectQualifier}AjaxWebLinks_WebLinks
where ModuleId = @ModuleId
GO

create procedure {databaseOwner}{objectQualifier}AjaxWebLinks_GetLink
@ModuleId int,
@LinkID int
as

select ModuleId,
LinkID,
LinkURL,
LinkText
from {objectQualifier}AjaxWebLinks_WebLinks
where ModuleId = @ModuleId
and LinkID = @LinkID
GO

create procedure {databaseOwner}{objectQualifier}AjaxWebLinks_AddLink
@ModuleId int,
@LinkURL nvarchar(100),
@LinkText nvarchar(100)
as

insert into {objectQualifier}AjaxWebLinks_WebLinks (
ModuleId,
LinkURL,
LinkText
)
values (
@ModuleId,
@LinkURL,
@LinkText
)

SELECT @@identity
GO

create procedure {databaseOwner}{objectQualifier}AjaxWebLinks_UpdateLink
@ModuleId int,
@LinkID int,
@LinkURL nvarchar(100),
@LinkText nvarchar(100)
as

update {objectQualifier}AjaxWebLinks_WebLinks
set LinkUrl = @LinkURL,
LinkText = @LinkText
where ModuleId = @ModuleId
and LinkID = @LinkID
GO

create procedure {databaseOwner}{objectQualifier}AjaxWebLinks_DeleteLink
@ModuleId int,
@LinkID int
as

delete
from {objectQualifier}AjaxWebLinks_WebLinks
where ModuleId = @ModuleId
and LinkID = @LinkID
GO
[/sourcecode ]

The DataProvider.cs is the abstract class containing the methods we will override in out SqlDataProvider.cs.

public abstract IDataReader GetLinks(int ModuleId);
public abstract IDataReader GetLink(int ModuleId, int LinkID);
public abstract int AddLink(int ModuleId, string LinkURL, string LinkText);
public abstract void UpdateLink(int ModuleId, int LinkID, string LinkURL, string LinkText);
public abstract void DeleteLink(int ModuleId, int LinkID);

The above methods will be used in our business layer to manage links. As you can see, each method requires  a module id to access records in the database. Each of the stored procedures contain a where clause which enforces this requirement.

The next file is the AjaxWebLinkInfo class which represents a single row in the AJaxWebLinks_WebLinks table.

    public class AjaxWebLinkInfo
    {
       public int ModuleId { get; set; }
       public int LinkID { get; set; }
       public string LinkURL { get; set; }
       public string LinkText { get; set; }
    }

The business layer class AjaxWebLinksController.cs will be responsible for executing the business layer methods in our webservice.

namespace DotNetNuke.Modules.AjaxWebLinks.Components
{

    /// -----------------------------------------------------------------------------
    /// <summary>
    /// The Controller class for AjaxWebLinks
    /// </summary>
    /// -----------------------------------------------------------------------------
    public class AjaxWebLinksController 
    {

        
        #region Public Methods
        public List GetLinks(int ModuleId)
        {
            return CBO.FillCollection(DataProvider.Instance().GetLinks(ModuleId));
        }

        public AjaxWebLinkInfo GetLink(int ModuleId, int LinkID)
        {
            return (AjaxWebLinkInfo)CBO.FillObject(DataProvider.Instance().GetLink(ModuleId, LinkID), typeof(AjaxWebLinkInfo));
        }

        public int AddLink(AjaxWebLinkInfo link)
        {
            
            if ( link.LinkText.Trim()!= string.Empty &amp;&amp; link.LinkURL.Trim() != string.Empty )
            {
               return DataProvider.Instance().AddLink(link.ModuleId,link.LinkURL, link.LinkText);
            }
            return 0;

        }

        public void UpdateLink(AjaxWebLinkInfo link)
        {
            if (link.LinkText.Trim() != string.Empty &amp;&amp; link.LinkURL.Trim() != string.Empty)
            {
                DataProvider.Instance().UpdateLink(link.ModuleId, link.LinkID, link.LinkURL, link.LinkText);
            }
        }

        public void DeleteLink(int ModuleId, int linkID)
        {
            DataProvider.Instance().DeleteLink(ModuleId, linkID);
        }


        #endregion


    }

}

The Webservice

After getting the typical data and business layer stuff out of the way it is time to look into the cool stuff. I created a traditional asp.net webservice called Handler.asmx and a wcf webservice called Handler.svc. Although these files have different names, the script generator created two classes with the same name so I renamed the wcf class wfHandler. Everything that I will be doing in this module is essentially a very simplified version of a solution I used in a real project so in my project I used the asmx webservice without any problems. However, I decided to try to get this module working with a wcf webservice. So far, I have not been able to get it working but will keep looking into it. I also do not know how to setup this up without having to create a serviceModel configuration inside of the DotNetNuke web.config (I am not sure that is acceptable for a distributable module). I will eventually want to do this in WCF so if anyone has any ideas please let me know.

This is what the Handler.asmx.cs file looks like:

namespace DotNetNuke.Modules.AjaxWebLinks
{
    /// <summary>
    /// Summary description for Handler
    /// </summary>
    [WebService(Namespace = "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    [System.ComponentModel.ToolboxItem(false)]
    // To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line. 
     [ScriptService]
    public class Handler : WebService
    {
        private AjaxWebLinksController controller;
        private AjaxSecurityContext securityContext;

        public Handler()
        {
            //Hook up security context
            securityContext = new AjaxSecurityContext(this.Context);
            //Get reference to business logic layer
            controller = new AjaxWebLinksController();
            //Then Have fun!!
           
        }


        [WebMethod]
        public List GetLinks()
        {
            return controller.GetLinks(securityContext.moduleInfo.ModuleID);
        }

        [WebMethod]
        public AjaxWebLinkInfo GetLink(int LinkID)
        {
            return controller.GetLink(securityContext.moduleInfo.ModuleID, LinkID);
        }

        [WebMethod]
        public int AddLink(AjaxWebLinkInfo link)
        {
            //Check if user has access to add stuff to the module if not throw an exception
            //This can be rewritten to send something back that javascript understands but I am not sure that is necessary
            //Would be nice to create an attribute to assert permissions [access(level= accesslevel.edit)]
            if (securityContext.canEdit)
            {
                throw new Exception("You do not have sufficient rights to perform this action");
            }
                //returns the id of the new link or 0 if failed
               return controller.AddLink(link);

        }

        [WebMethod]
        public void DeleteLink(int LinkID)
        {
            //Check if user has access to add stuff to the module if not throw an exception
            //This can be rewritten to send something back that javascript understands but I am not sure that is necessary
            //Would be nice to create an attribute to assert permissions [access(level= accesslevel.edit)]
            if (securityContext.canEdit)
            {
                throw new Exception("You do not have sufficient rights to perform this action");
            }
   
            controller.DeleteLink(securityContext.moduleInfo.ModuleID, LinkID);

        }


    }
}

The real magic happens in the webservice constructor which passes on the current http context to a new instance of the AjaxSecurityContext class (IWebLite). In the AddLink and DeleteLink methods we use the security context to figure out if the client has the right permissions before we all them to do something. For those who are wondering, the view permissions are checked inside of the AjaxSecurityContext file. This means that execution will never even get to a WebMethod if the client does not have at least view permissions.

My next post will go into detail about the AjaxSecurityContext.cs file and subsequent posts will talk about the client side of things.

Dotnetnuke Jquery Weblinks Module Database design


This is a continuation from my last post: Implementing jQuery ajax webservices in Dotnetnuke. At the end of that post I said that I was not sure that what I set out to do would work. However, since then I was able to prove that it can work. From now on I will be using the Ajax WEBLINKS project to demonstrate my method for using jQuery, Ajax and Asp.net webservices  in Dotnetnuke in place of edit pages.

Plugin Wireframe

I am assuming persons reading this post are already familiar with the process of creating a Dotnetnuke module so I will not go into detailed steps for creating our WEBLINKS module. Below is a mockup of what our WEBLINKS module might look like on our Dotnetnuke website. The basic idea with this module is each tab can have its own set of weblinks with its own permissions.

Naturally, the add, edit and delete buttons will only appear for people with the appropriate permissions. Then webservice will also check the user’s role before executing  ajax method calls. I could go crazy with this and add separate permissions for adding, editing and deleting then add the option for admin to approve WEBLINKS; but that would require me to create a settings interface. Unfortunately, I don’t have enough time to do all of that. However, it is possible.

Designing the database

The following image is a rough design of the table which will store the WEBLINKS. I may need to change this design later on but it will do for now.

I created a database diagram which shows the relationships between the modules, tabs and their permissions to ensure my new entity fits in the Dotnetnuke database structure. The diagram only shows keys in some of the tables because I was mostly interested in the entity relationships.

I am not sure why there is a DesktopModules and a ModuleDefinitions table. Seems like these should be one table but maybe there is something I am not seeing. The modules table is what will hold the individual instances of our WEBLINKS module. This is where the values for our ModuleID foreign key will come from. This will allow each instance of our WEBLINKS module to have its own set of links.

Now that our module and database is sketched out, it is time to head to visual studio and get our hands dirty. My next post will cover the process of creating the skeleton for our WEBLINKS module.

 

Implementing jQuery ajax webservices in Dotnetnuke.


I have been looking into Dotnetnuke for the past couple of weeks in preparation for a new client project which requires Dotnetnuke. One of the questions posed by my team was, how will we make use of modern standards such as Ajax and jQuery? I was specifically interested in the ability to make ajax calls using jquery while still maintaining our Dotnetnuke security context. I did my usual Google search and found a few examples that seemed to be going in the right direction. Then, I came across the iweblite project on codeplex which seemed to do exactly what I wanted.

This post and the next few posts will explore the use of jQuery/Ajax and asp.net and wfc webservices as alternatives to regular Dotnetnuke edit pages. In the end I should have a working module which demonstrates a secure but unobtrusive way for developers to use jQuery/Ajax in Dotnetnuke. However, At this point I have no idea if any of this will work.

I drew up the diagram belong to illustrate my understanding of the Dotneuke system and how a webservice script currently fits into the picture. The yellow tile in the middle represents the entire Dotnetnuke installation, the purple tiles at the top are modules installed by the host, the purple portal tiles are different websites hosted within the Dotnetnuke installation, the blue page tiles are Dotnetnuke pages/tabs which are essentially containers for a bunch of instances of the modules defined above.

Dotnetnuke application layout

Let us create a sample module called WEBLINKS from our illustration

Dotnetnuke separates the definition of a module from its actual placement. So Although WEBLINKS=300 and WEBLINKS=99 are of the same module WEBLINKS(defid=2), they are treated differently by Dotnetnuke.  WEBLINKS(defid=2) is designed so that each instance has its own data. This means that WEBLINKS=300 on tabid=2 will show a different set of links from WEBLINKS=99 on tabid=1.

Let us create a user called USERA and give him access to edit/view WEBLINKS=300 on tabid=2 but not on WEBLINKS=99. This means that USERA will see WEBLINKS on tabid=2 but not tabid=1. We want to make our WEBLINKS module cool by adding in some ajax functionality. So rather than the usual dotnetnuke edit page, we will allow users to add a new weblink right on the module view page. We will achieve this by using Jquery to send a JSON packet containing the details of our new weblink directly to our AjaxHandler.asmx or AjaxHandler.svc. We expect that when USERA has added his new weblink it should only appear in WEBLINKS=300 on tabid=2. We do not want him to have the ability to add weblinks to WEBLINK=99 on tabid=1. We will probably provide use a modal or slide down form for to input the new weblink.

The challenge with calling a webservice in this scenario is figuring out the context in which the request was made. This is where iWeblite comes in. It uses the request url to find the portal id (There are potential  issues with having the client provide the portalid) so all we need to get from the client are the tabid and the tabmoduleid for us to get the user’s security context. So theoretically, we should be able to make a call to AjaxHandler.asmx using Jquery with our tabid, tabmoduleid and other module specific parameters and get the same result we would from a postback.

My next post will talk about the design and basic functionality of the WEBLINKS plugin I will use to prove this concept. Again I have no idea whether or not I will fail in proving this to be a viable option for module development in Dotnetnuke.