Gooddogs Development Blog

WCF Series: Part #6 Exposing DotNetNuke Module Functionality with WCF

Feb 13

Written by:
2/13/2011 4:45 PM  RssIcon

In this section of the “DotNetNuke and WCF” series, I will show how you can use everything you’ve learned to expose some Module “functionality”. So far, we’ve used WCF to expose some Data about our web site. But hopefully you can see that we are not limited to just exposing data, but that we can also expose module functionality using the same methods.

I’m going to use the Repository module as an example. This approach can be used with any module, but for this example, the Repository module is a good choice as it does have some ‘administrative’ functions that we provide offline.

You can configure the Repository module to allow user uploads, and optionally require user uploads to be moderated. In this WCF example, we will add an additional OperationContract to expose the module’s data, and write a desktop Forms application that will monitor our web site and alert us when a user uploads a new file to our module. We will also add an OperationContract that will allow us to to approve that upload right from our desktop application without ever having to visit our web site.

This is just a simple example, but hopefully you’ll think about the modules you are using on your sites and this will spark some ideas on how to use WCF to extend your content administration to your desktop, providing the ability to monitor your site and even administer your content from a desktop or mobile application.

So, let’s get started

Since we are hosting our WCF service within our DotNetNuke web site, we have full access to not only the DotNetNuke namespace and assembly, but any modules that are installed as well.

Here’s the plan for our service… we’ll first check to see if the Repository module is installed on the site, then if it is, we’ll check all of the Repository modules on our site to see if there are any uploads that require moderation, and finally, we’ll provide a method to approve the upload.

We’ll need two OperationContracts, GetUnapprovedRepositoryFiles() and ApproveRepositoryFile()

We’ll also need to create a DataContract, so that our service can provide complex data objects (Repository data items), as well as control what information is sent from the service back to the client (provide the minimum data that the client will need to do the job).

Creating the OperationContacts:

Open the IDNNService.vb interface file and add the following…

First, one of our methods will return a List of items, so let’s take advantage of Generic Lists and import the Generics namespace

 1: Imports System.Collections.Generic

Then, we add our two new OperationContracts()

 1: <OperationContract()> _ 
 2: <WebGet(ResponseFormat:=WebMessageFormat.Xml, _ 
 3: UriTemplate:="metrics")> _ 
 4: Function GetMetrics() As Metrics 
 5: 
 6: <OperationContract()> _ 
 7: <WebGet(ResponseFormat:=WebMessageFormat.Xml, _ 
 8: UriTemplate:="repository/get/{portalid}")> _ 
 9: Function GetUnapprovedRepositoryFiles(ByVal portalid As String) _ 
 10: As List(Of RepositoryFile) 
 11: 
 12: <OperationContract()> _ 
 13: <WebGet(ResponseFormat:=WebMessageFormat.Xml, _ 
 14: UriTemplate:="repository/approve/{itemid}")> _ 
 15: Function ApproveRepositoryFile(ByVal itemid As String) As Boolean

Take a look at the UriTemplates above. It is convenient to adopt a naming convention for your URIs. I usually start my URIs with the resource I am targeting, in this case the Repository module, then I follow with the verb ( what actionI want to perform ), then a resource identifier to pinpoint which resource item, or items, I want to perform that action on, and finally if there is any further clarification of the items I want to work upon.

So, for example, /repository/get/{portalid}/pending, indicates that I am addressing a repository resource, I am performing a 'get' on that resource, I am constraining the collection to a specific portal id, and finally only those items with a status of pending.

In the second example, /repository/approve/{itemid}, I am again addressing a repository resource, I will be performing an 'approve' on that resource and finally passing the unique 'itemid' that will identify which repository resource I want to approve. If you get in the habit of constructing your URIs in that manner, your API and RESTful interface will feel very comfortable to work with.

Creating the DataContract:

Next, we need to define our DataContract(). We take a look at the Repository data schema and figure out which data elements we need to return. Having done that, we define our Public Class RepositoryItems and attribute it to mark it as a DataContract. We don’t need to return the entire Repository schema, we can decide which fields are going to be necessary for an offline administrator to review and approve the upload. So, in this case, we need the ItemID, the Title, Description and the User who uploaded it. Of course, if you decide you need more data, just add more [DataMember]s to your DataContract.

 1: <DataContract()> _ 
 2: Public Class RepositoryFile 
 3: 
 4: <DataMember()> _ 
 5: Public ItemID As Integer 
 6: <DataMember()> _ 
 7: Public Title As String 
 8: <DataMember()> _ 
 9: Public Description As String 
 10: <DataMember()> _ 
 11: Public User As String 
 12: 
 13: End Class

Now that we’ve defined our interface, we move on to write the implentation. Open the DNNService.vb file. We’ll have to write three functions to implement the two new OperationContracts we just defined, one to return the list of unapproved items, one to approve an item and a private method to find all the Repository modules on my site.

The Implementation:

The first OperationContract we need to implement is the GetUnapprovedRepositoryFiles(portalid). This function will find all of the Repository modules in the portal specified by the portalid parameter (using our private function), then return each item that requires moderation in a List of RepositoryFile objects . Since we’re running this WCF service within the DotNetNuke context, we can simply add a reference to DotNetNuke.Modules.Repository.dll.


 1: Public Function GetUnapprovedRepositoryFiles(ByVal portalid _
       As String) _ 
 2: As List(Of RepositoryFile) _ 
 3:    Implements IDNNService.GetUnapprovedRepositoryFiles 
 4: 
 5:    Dim results As New List(Of RepositoryFile) 
 6:    Dim pID As Integer = Int32.Parse(portalid) 
 7: 
 8:    Dim repController As New Repository.RepositoryController 
 9:    Dim modules As List(Of ModuleInfo) = GetRepositoryModules(pID) 
 10: 
 11:   For Each item As ModuleInfo In modules 
 12: 
 13:      Dim items As ArrayList = _
             repController.GetRepositoryObjects( _ 
 14:         item.ModuleID, _ 
 15:         "", "CreatedDate", 0, -1, "", -1) 
 16: 
 17:      For Each repositoryItem As Repository.RepositoryInfo In _
             items 
 18: 
 19:         Dim newItem As New RepositoryFile() With { _ 
 20:           .ItemID = repositoryItem.ItemId, _ 
 21:           .Title = repositoryItem.Name, _ 
 22:           .Description = repositoryItem.Description _ 
 23:         } 
 24: 
 25:         Dim user As UserInfo = UserController.GetUserById(pID, _ 
 26:            repositoryItem.CreatedByUser) 
 27: 
 28:         newItem.User = user.Username 
 29: 
 30:         results.Add(newItem) 
 31: 
 32:     Next 
 33: 
 34:   Next 
 35: 
 36:   Return results 
 37: 
 38: End Function
We use the reference to the DotNetNuke.Modules.Repository.dll to get a RepositoryController object (line 8) and then execute the private GetRepositoryModules(portal) function (line 9) to get a List of all of the Repository module instances in that portal. We then use a For Each loop to look at each module and use the .GetRepositoryObjects() function (line 13) to get an array of Repository items that have not been approved (the 4th parameter, –1, indicates that we want unapproved items).

As we get each item, we create a new RepositoryFile (line 19) object, copy the data into our new object (lines 20-22), then use the DotNetNuke UserController to get the username of the user who uploaded the item (line 25). Then we add our new RepositoryFile item to the List that we will eventually be returning (line 30). When we’re all done, we return the List (line 36).

Finally, we write the implementation code for our other OperationContact, approve. In addition, we’ll write a private function that will traverse all the tabs in my portal and find all the instances of Repository modules.

 1: Public Function ApproveRepositoryFile(ByVal itemid As String) As Boolean _ 
 2:    Implements IDNNService.ApproveRepositoryFile 
 3: 
 4:    Dim results As Boolean 
 5: 
 6:    Dim repoController As New Repository.RepositoryController 
 7:    Try 
 8:      repoController.ApproveRepositoryObject(Int32.Parse(itemid)) 
 9:      results = True 
 10:   Catch 
 11:     results = False 
 12:   End Try 
 13: 
 14:   Return results 
 15: 
 16: End Function 
 17: 
 18: Private Function GetRepositoryModules(ByVal portalid As Integer) _ 
 19:    As List(Of ModuleInfo) 
 20: 
 21:    Dim mc As New ModuleController 
 22:    Dim tc As New Entities.Tabs.TabController 
 23:    Dim objModule As ModuleInfo 
 24:    Dim lModules As New List(Of ModuleInfo) 
 25: 
 26:    ' get a list of all of the Repository Modules installed in this 
 27:    ' portal 
 28: 
 29:    Dim repModInfo As ModuleDefinitionInfo = _ 
 30:    ModuleDefinitionController.GetModuleDefinitionByFriendlyName( _ 
 31:       "Repository") 
 32: 
 33:    Dim package As PackageInfo = _ 
 34:     PackageController.GetPackageByName("DotNetNuke.Repository") 
 35: 
 36:    Dim tabsWithModule As IDictionary(Of Integer, Entities.Tabs.TabInfo) = _ 
 37:      tc.GetTabsByPackageID(portalid, package.PackageID, False) 
 38: 
 39:    ' first, get a list of all the tabs that contain at least one repository 
 40:    For Each tab As Entities.Tabs.TabInfo In tabsWithModule.Values 
 41: 
 42:      ' for each tab, get the repository module info 
 43:      Dim modules As Dictionary(Of Integer, ModuleInfo) = _ 
 44:      mc.GetTabModules(tab.TabID) 
 45: 
 46:      For Each objModule In modules.Values 
 47:        If objModule.ModuleDefID = repModInfo.ModuleDefID Then 
 48:          lModules.Add(objModule) 
 49:        End If 
 50:      Next 
 51: 
 52:    Next 
 53: 
 54:    Return lModules 
 55: 
 56: End Function

Let’s see what we have so far…

For testing, I have placed a Repository module on a page in my web site and uploaded an item using a guest account so the upload will require moderation

image

And now, I log in using my admin account, and if I view my Repository module, you’ll see that there are no items showing, however I see there is 1 item requiring moderation.

image

So far so good … Now let’s test out our WCF service. If I open a browser and browse to my WCF service URL, and add the rest URI template that will return a list of unapproved RepositoryFile objects.

 1: http://localhost/dnn531/desktopmodules/repository/
    dnnservice.svc/rest/repository/get/0

I will get the following XML returned

 1: <ArrayOfRepositoryFile xmlns="http://schemas.datacontract.org/2004/07/" 
 2: xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> 
 3: <RepositoryFile> 
 4: <Description><p>This is Whiskey Fabian. He's a 7 year old terrier mix</p></Description> 
 5: <ItemID>3</ItemID> 
 6: <Title>Whiskey</Title> 
 7: <User>JohnSmith</User> 
 8: </RepositoryFile> 
 9: </ArrayOfRepositoryFile>

Smile. Damn, this WFC stuff is easy!

We now have a WCF service that provides the 2 functions we need to do offline moderation.

In the next section, we’ll write a desktop application that will use our WCF service to check for uploaded files that require moderation, allow us to review the uploads and approve them, all without ever visiting our web site!

Tags:
Categories: