Over the past 6 years I have watched as DotNetNuke matured into a well-rounded platform that is capable of meeting the needs of a wide variety of individuals and organizations. One of the keys to the platform’s success is that it continues to leverage technology to make it easy to build sites the way you want.
A user recently asked me how they could re-use the content from one page on another page in their site. A couple of years ago I would have suggested that they make a “copy” of the module using the built-in capabilities. This works well when you are trying to duplicate the entire content of the module, however, there are times when you just want a portion of the content. In the past the answer would have been to split up the content on the original page – which might require changing your skin and may not be possible when using 3rd party modules.
The optimal solution would not require me to make any changes to the original page, but would instead allow me just to grab specific content and display it on a new page. Thanks goodness we added jQuery support in 4.9 and 5.0. This problem is really easy to solve with some simple html and a jQuery one-liner.
For a simple scenario, lets assume I want to copy the quicklinks from default template on my site’s homepage (I’m using the standard DotNetNuke install so you can follow along). The quicklinks are a subset of the content from an HTML module on the home page.
Add a test page to your site so that we can see how this works. The test page should come pre-installed with an HTML module. If your copy does not, go ahead and add one now… don’t worry, I’ll wait for you to finish.
Now we edit the content in our HTML module and place a simple div in our content.
<div id="placeholderId" >This content will be replaced.</div>
I use a simple trick that Kevin Schreiner showed me a couple years ago to inject a script into my page without worrying about the HTML module stripping it out. Open the module settings and expand the “Advanced Settings” node to display the module Header and Footer items. We’ll place our JavaScript into the Header which is not altered when it is injected into the page.
The following JavaScript will run after the page has finished loading.
<script type="text/javascript">
jQuery(function($){
$("#placeholderId")
.load("http://localhost/dnn511pe/Default.aspx #QuickLinks");
});
</script>
The $("#placeholderId") line gets a reference to the element we defined in our HTML. The .load method, makes an AJAX call to retrieve the home page, and then finds the content that matches the #QuickLinks selector.
All of this works really well, but this is DotNetNuke and we can make this much easier. So lets create a simple module that handles all this drudgery for us. I’m going to call this module ContentGrabber.
The ContentGrabber module has a simple view page that will inject our placeholder and JavaScript. We also add a settings page because it wouldn’t be very interesting if we could only grab the quicklinks from the home page.
My view page has very little markup. One change I did make to our original script and HTML was to add a few code expressions to allow us to specify different pages and jQuery selectors. We also made sure to generate a unique content ID so that we could add multiple modules to the same page.
<%@ Control language="vb"
Inherits="DotNetNuke.Modules.ContentGrabber.ViewContent"
CodeFile="ViewContent.ascx.vb"
AutoEventWireup="false" Explicit="True" %>
<% If Not String.IsNullOrEmpty(PageURL) Then%>
<script type="text/javascript">
jQuery(function($) {
$("#contentGrabber_<%=moduleid %>")
.load("<%=PageURL %> <%=Query %>");
});
</script>
<% End If%>
<div id="contentGrabber_<%=moduleid %>">
Update the module settings to replace this content.
</div>
Now the only thing left to do is add some code to the code-behind to retrieve the PageURL and Query values from our module settings. Notice that there are really only two lines of actual code, beyond the boilerplate namespace, class and property declarations.
Imports System
Namespace DotNetNuke.Modules.ContentGrabber
Partial Class ViewContent
Inherits Entities.Modules.PortalModuleBase
Public ReadOnly Property PageURL() As String
Get
Return CType(Me.Settings(GRABBER_URL), String)
End Get
End Property
Public ReadOnly Property Query() As String
Get
Return CType(Me.Settings(GRABBER_QUERY), String)
End Get
End Property
End Class
End Namespace
So now that our view page is complete, lets create a page to handle editing our settings.
<%@ Control language="vb"
Inherits="DotNetNuke.Modules.ContentGrabber.Settings"
CodeFile="Settings.ascx.vb"
AutoEventWireup="false"
Explicit="True" %>
<%@ Register TagPrefix="dnn"
TagName="Label"
Src="~/controls/LabelControl.ascx" %>
<table width="550" cellspacing="0" cellpadding="4" border="0" width=100%>
<tr>
<td class="SubHead" width="150" valign="top">
<dnn:label id="plPage" controlname="txtPage" runat="server" />
</td>
<td>
<asp:TextBox ID="txtPage" runat="server" Width="300"></asp:TextBox>
</td>
</tr>
<tr id="rowWorkflow" runat="server">
<td class="SubHead" width="150" valign="top">
<dnn:label id="plQuery" controlname="txtQuery" runat="server" />
</td>
<td>
<asp:TextBox ID="txtQuery" runat="server" Width="300"></asp:TextBox>
</td>
</tr>
</table>
There is not much interesting going on here. Yes, yes. I know. I used a table. It’s ok. The W3C hasn’t completely deprecated that tag yet. If you would prefer to be semantically correct then feel free to use divs and CSS. We’ll wait the extra 10 minutes for you to catch up.
OK. Hopefully I didn’t lose any readers over that whole table thing. Let’s move on the code-behind.
Imports System
Imports System.Web.UI
Namespace DotNetNuke.Modules.ContentGrabber
Partial Class Settings
Inherits DotNetNuke.Entities.Modules.ModuleSettingsBase
Public Overrides Sub LoadSettings()
Try
If Not Page.IsPostBack Then
txtPage.Text = CType(ModuleSettings(GRABBER_URL), String)
txtQuery.Text = CType(ModuleSettings(GRABBER_QUERY), String)
End If
Catch exc As Exception 'Module failed to load
ProcessModuleLoadException(Me, exc)
End Try
End Sub
Public Overrides Sub UpdateSettings()
Try
' update modulesettings
Dim objModules As New DotNetNuke.Entities.Modules.ModuleController
objModules.UpdateModuleSetting(ModuleId, GRABBER_URL, txtPage.Text)
objModules.UpdateModuleSetting(ModuleId, GRABBER_QUERY, txtQuery.Text)
Catch exc As Exception 'Module failed to load
ProcessModuleLoadException(Me, exc)
End Try
End Sub
End Class
End Namespace
This code was a little more involved, but still pretty simple. I’ll leave it up to the user to create the associated resource file for our labels. At this point we have a fully working module that packages up our original script in a nice little module with settings to handle the customizable behavior. As you can see in the image below, both approaches provide the same results.
I have left a few exercises for the reader.
- Substitute the URL control for the URL setting.
- Add an AJAX waiting image while loading the HTML.
- Allow the user visually select the page element(s) they want to copy.
Download the working module from CodePlex.
Technorati Tags:
dotnetnuke,
jquery,
ajax