The IPortable interface will allow the users of your module to develop content on a development server and easily deploy this content to a production server. You will want to implement this interface in practically every module you create no matter how simple it is. The good news is that this task is very easy to do and does not require advanced programming or a significant amount of time.
What IPortable Will Do For You
Let's say you have a development server at your company and you created a survey using the Survey module (using the 04.00.00 or higher version. A link to the Beta is available at the end of this article).
You now want to put the Survey on your production server. To do so you would simply follow these steps:
1) From the menu of the Survey module select Export Content
2) Next, from the Export Module page click the Export button
3) While logged in as the portal Administrator select File Manger from the Admin menu
(do this while logged in as portal Administrator not as the portal Host using the Host account because you will not see the correct directory in the File Manager)
We see the .xml file that has been created (in this example content.Survey.Survey.xml)
4) Import the .xml file to your production server using these steps:
- Log in as the Administrator of the portal
- From the Admin menu select File Manger
- In the File Manager click Upload
- Browse to the location of the .xml file and after ensuring that Portal Root is selected in the location drop-down, click Upload New File
5) Next place an instance of the Survey module on a page (click here for directions on adding a module to a page) and from the Survey module's menu select Import Content
6) The .xml file is show in the drop-down. Click the Import button
And you're done!
Implementing IPortable
To implement this functionality in your own custom DotNetNuke module you simply:
- Specify a business controller class in the module definition for the module
- Add the line Implements Entities.Modules.IPortable in the business controller class
- Create a ExportModule method and an ImportModule method
Specify a business controller class
The first thing you need to do is indicate a business controller class in the module definition for your module (If you have already started working on your module and you neglected to do this step initially you will have to delete the module definition and recreate it).
Here are the steps I used to create the module definition for the Survey module:
While logged into the DotNetNuke site as the "host" account, from the menu bar I selected Host and then selected Module Definitions
I clicked on the black arrow that is pointing down to make the fly-out menu to appear. On that menu I selected Add New Module Definition
In the Edit Module Definitions page I entered:
- DNN_Survey for MODULE NAME
- Survey for FOLDER TITLE
- Survey for FRIENDLY TITLE
- Survey allows you to create custom surveys to obtain public feedback for DESCRIPTION
- 04.00.00 for VERSION
- DotNetNuke.Modules.Survey.SurveyController for Controller Class
I then clicked UPDATE and completed the module definition (see my tutorial that describes how to create a module definition)
Add the Line Implements Entities.Modules.IPortable to the Business Controller Class
I created a SurveyController.vb class file
In that class I added the line Implements Entities.Modules.IPortable
Visual Studio then automatically adds the two stub methods.
Public Function ExportModule(ByVal ModuleID As Integer) As String Implements Entities.Modules.IPortable.ExportModule
End Function
Public Sub ImportModule(ByVal ModuleID As Integer, ByVal Content As String, ByVal Version As String, ByVal UserID As Integer) Implements _ Entities.Modules.IPortable.ImportModule
End Sub
Create a ExportModule method
When the users of your module selects Export Content, the DotNetNuke framework will call the ExportModule method that you create and pass it a ModuleID parameter. It is expecting your method to return an .xml formatted response as a String data type.
When implementing this for the Survey module I can see that the Surveys are contained in the Surveys table, but the options (for example "First Choice") are contained in the SurveyOptions table.
For each Survey in the Surveys table, I will need to export the matching options for that survey in the SurveyOptions table. Another thing to notice is that while the primary key for the Surveys table is SurveyID, the field that is important right now is the ModuleID field. In the DotNetNuke architecture the ModuleID is an important parameter. A ModuleID represents a single instance of a module and is used to segment the data for each instance of that module in the database. A ModuleID is unique across all portals in a DotNetNuke installation.
Here is my implementation of the ExportModule method:
Public
Function ExportModule(ByVal ModuleID As Integer) As String Implements DotNetNuke.Entities.Modules.IPortable.ExportModule
Dim strXML As New StringBuilder()
Dim settings As New XmlWriterSettings()
settings.Indent = True
settings.OmitXmlDeclaration = True
Dim Writer As XmlWriter = XmlWriter.Create(strXML, settings)
'Outer Loop - To build the Surveys
Dim colSurveys As List(Of SurveyInfo) = GetSurveys(ModuleID)
If colSurveys.Count > 0 Then
Writer.WriteStartElement("surveys")
Dim SurveyInfo As SurveyInfo
For Each SurveyInfo In colSurveys
Writer.WriteStartElement("survey")
Writer.WriteElementString("question", SurveyInfo.Question)
Writer.WriteElementString("vieworder", SurveyInfo.ViewOrder)
Writer.WriteElementString("createdbyuser", SurveyInfo.CreatedByUser)
Writer.WriteElementString("createddate", SurveyInfo.CreatedDate)
Writer.WriteElementString("optiontype", SurveyInfo.OptionType.ToString)
'Inner Loop - To build the Options for each Survey
Dim colSurveyOptions As List(Of SurveyOptionInfo) = SurveyOptionController.GetSurveyOptions(SurveyInfo.SurveyId)
If colSurveyOptions.Count > 0 Then
Writer.WriteStartElement("surveyoptions")
Dim SurveyOptionInfo As SurveyOptionInfo
For Each SurveyOptionInfo In colSurveyOptions
Writer.WriteStartElement("surveyoption")
Writer.WriteElementString("optionname", SurveyOptionInfo.OptionName)
Writer.WriteElementString("iscorrect", SurveyOptionInfo.IsCorrect)
Writer.WriteElementString("vieworder", SurveyOptionInfo.ViewOrder)
Writer.WriteEndElement()
Next ' Retrieve the next SurveyOption
Writer.WriteEndElement()
End If
Writer.WriteEndElement()
Next
Writer.WriteEndElement()
Writer.Close()
Else
' There is nothing to export
Return String.Empty
End If
Return strXML.ToString()
End Function
Here is a sample of the output created by the DotNetNuke framework from the output of this method:
<?xml version="1.0" encoding="utf-8" ?>
<content type="Survey" version="04.00.00">
<surveys>
<survey>
<question><![CDATA[Question Number One]]></question>
<vieworder><![CDATA[-1]]></vieworder>
<createdbyuser><![CDATA[-1]]></createdbyuser>
<createddate><![CDATA[9/3/2006 4:46:45 PM]]></createddate>
<optiontype><![CDATA[R]]></optiontype>
<surveyoptions>
<surveyoption>
<optionname><![CDATA[First Choice]]></optionname>
<iscorrect><![CDATA[False]]></iscorrect>
<vieworder><![CDATA[0]]></vieworder>
</surveyoption>
<surveyoption>
<optionname><![CDATA[Second Choice]]></optionname>
<iscorrect><![CDATA[False]]></iscorrect>
<vieworder><![CDATA[1]]></vieworder>
</surveyoption>
</surveyoptions>
</survey>
</surveys>
</content>
Create a ImportModule method
When the users of your module select Import Content, the DotNetNuke framework will call the ImportModule method that you create and pass it a ModuleID parameter and a Content parameter as well as Version and UserID. It is expects your controller class to process the contents of the Content parameter which has a string data type. The Content parameter will contain the data in the form of the sample above. Your ImportModule method will not return anything. Your method will usually just insert the data passed to in in the Content parameter into the databse.
Here is my implementation of the ImportModule method:
Public
Sub ImportModule(ByVal ModuleID As Integer, ByVal Content As String, ByVal Version As String, ByVal UserId As Integer) Implements DotNetNuke.Entities.Modules.IPortable.ImportModule
'Import the Surveys
'Outer Loop - To insert the Surveys
Dim intCurrentSurvey As Integer
Dim xmlSurvey As XmlNode
Dim xmlSurveys As XmlNode = GetContent(Content, "surveys")
For Each xmlSurvey In xmlSurveys
Dim SurveyInfo As New SurveyInfo
SurveyInfo.ModuleId = ModuleID
SurveyInfo.Question = xmlSurvey.Item("question").InnerText
SurveyInfo.ViewOrder = xmlSurvey.Item("vieworder").InnerText
SurveyInfo.CreatedByUser = xmlSurvey.Item("createdbyuser").InnerText
SurveyInfo.CreatedDate = xmlSurvey.Item("createddate").InnerText
SurveyInfo.OptionType = xmlSurvey.Item("optiontype").InnerText
'Add the Survey to the database
intCurrentSurvey = AddSurvey(SurveyInfo)
'Inner Loop - To insert the Survey Options
Dim xmlSurveyOption As XmlNode
Dim xmlSurveyOptions As XmlNode = xmlSurvey.SelectSingleNode("surveyoptions")
For Each xmlSurveyOption In xmlSurveyOptions
Dim SurveyOptionInfo As New SurveyOptionInfo
SurveyOptionInfo.SurveyId = intCurrentSurvey
SurveyOptionInfo.OptionName = xmlSurveyOption.Item("optionname").InnerText
SurveyOptionInfo.IsCorrect = xmlSurveyOption.Item("iscorrect").InnerText
SurveyOptionInfo.ViewOrder = xmlSurveyOption.Item("vieworder").InnerText
'Add the Survey to the database
SurveyOptionController.AddSurveyOption(SurveyOptionInfo)
Next
Next ' Retrieve the next Survey
End Sub
That's it. You're done.
It is not a lot of code, yet the functionality that this creates for the end user is significant.
Hopefully you will find this example helpful because if you examine the code you will see that I had to insert a survey, retrieve the SurveyID and then use that SurveyID when inserting the matching survey options.
This interface represents another example of why you would choose to use the DotNetNuke framework for a project rather than coding the project completely from scratch. By simply creating two simple methods you can provide your project with a process to move content between servers. Unlike the sometimes awkward and error prone methods of direct data transfer, you will have .xml files that can be easily backed up and versioned. Most importantly the data is separated from the content (for example the survey results are not exported just the surveys). In addition you have complete control over what defines content.
In future postings I will cover other Interfaces that are important to module developers such as IActionable and ISearchable,
The Survey code covered above is BETA and has not been released. You can download the code using this link. Before you upload it to an existing DNN4 site you have to remove these two assemblies from the "\bin" directory of your existing DNN site.
DotNetNuke.Modules.Survey.dll
DotNetNuke.Modules.Survey.SqlDataProvider.dll