Learn More





DNN Community Blog

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.

Using DotNetNuke Service Framework for Cross Site Content Sharing

ContentSharingIn this article I’ll explain how to use DotNetNuke’s service framework in order to share content across multiple installations of DotNetNuke. This same methodology will allow you to share your content housed within DotNetNuke to other applications built on any stack outside of the .net ecosystem. Pretty useful right?

What can I use this for?

Anything honestly. What content you want to share is totally up to you! This is can be useful for departments within a company with their own installations of DotNetNuke to share content to a central intranet or a public website. Another consideration In the age of social you may want to allow users to list their recent journal activities on their own site. This kind of sharing is a win win for you and your user. Your user will get freedom of their information and you will get link back traffic for wherever they utilize this content.

Setting the stage – Sharing the most recent pages that have been created.

I know my example might not be the most useful feature in the world but I hope it will inspire you to see how you can implement this into your own content. The result of this module will be to share the most recent pages created within your portal and use a little Javascript snippet to view that data

Creating the service

imageYou need a RouteMapper, Controller and ideally an Entity class to return as a collection. This entity collection will be serialized to a JSON object to be passed to the client. I won’t dive into the specifics as to how these functions work but my hope is there is enough info here to get you started.


public class PageSharingRouteMapper : IServiceRouteMapper
    public void RegisterRoutes(IMapRoute mapRouteManager)
		new[] { "TwentyTech.PageSharing" });


[DnnAuthorize(AllowAnonymous = true)]
public class PageSharingController : DnnController
    public ActionResult GetRecentPages()
            List pages = 
		TabController.GetPortalTabs(Globals.GetPortalSettings().PortalId, -1, false, false)
			.Select(page => new SimplePageInfo(page)).ToList();

            return Json(pages);
        catch(Exception ex)
            return Json("Error");

[DnnAuthorize(AllowAnonymous = true)] attribute is important because it will allows unauthenticated users to be able to access the functions within the controller.


public class SimplePageInfo
    public SimplePageInfo(TabInfo page)
    public string PageName { get; set; }
    public string PageUrl { get; set; }
    public DateTime DateCreated { get; set; }
    public string CreatedByUser { get; set; }

    public void Fill(TabInfo page)
        PageName = page.TabName;
        PageUrl =Globals.NavigateURL(page.TabID);
        DateCreated = page.CreatedOnDate;
        UserInfo user = UserController.GetUserById(Globals.GetPortalSettings().PortalId, page.CreatedByUserID);
            CreatedByUser = "Unknown";
            CreatedByUser = user.DisplayName;

You may be wondering “Why not just return the TabInfo as the Entity?”. Not all the entities within DotNetNuke can be serialized to JSON and may have reciprocal calls which prohibits serialization. For example the taxonomy properties.  The other reason is simply you may not want to expose all the data from the entire entity for security considerations or data performance reasons to name a couple.

Getting the data – Server

CORS will deny you from doing a cross site scripting (XSS) request to your other domains. Chrome & Firefox implement CORS and simple header Access-Control-Allow-Origin added primary domain’s application with a value of * will grant access to your data or you may specify specific domains. See examples:

Internet explorer will work as well but requires an exception which will see in the client side coding next.

Getting the data – Client

The client can be anywhere on any application. With a little jQuery, Knockout, and some browser specific coding we can pull in our list of pages. In my case I’ve used the Razor Module in order to incorporate the script and UI.

<script src="/Resources/Shared/scripts/knockout.js"></script>

<ul data-bind="foreach: pages">
    <li data-bind="text: PageName"></li>
<script type="text/javascript">
    $(document).ready(function () {
       var pageUrl = "http://pagesharing.loc/DesktopModules/TwentyTech/PageSharing/API/PageSharing.ashx/GetRecentPages"
        GetRecentPages = function () {
            if (window.XDomainRequest) {
                xdr = new XDomainRequest();
                xdr.onload = function () {
                    var pages = JSON.parse(xdr.responseText);
                    var viewModel = {
                        items: ko.observableArray(pages)
      "POST", pageUrl);
            else {
                    type: "POST",
                    url: pageUrl,
                }).done(function (pages) {
                    if ($.browser.mozilla) {
                        pages = JSON.parse(pages);

                    var viewModel = {
                        pages: ko.observableArray(pages)
                }).fail(function (xhr, result, status) { });



Things to watch for

Always check every browser for your expected output. As you can see from my above script I’ve had to implement exceptions for each of the major browsers. Internet Explorer’s lack of CORS support requires using a proprietary object function XDomainRequest and Firefox not returning the object as JSON requires a manually invocation to serialize the string to JSON.

You will also need to note that XDomainRequest only passes data with the content type text/plain. This will require some defensive coding within your web service to get the data being passed in.

public ActionResult GetPagesByName(string pageName)
        string documentContents;
        using (Stream receiveStream = Request.InputStream)
            using (StreamReader readStream = new StreamReader(receiveStream, Request.ContentEncoding))
                documentContents = readStream.ReadToEnd();
        pageName= Server.HtmlDecode(documentContents.Replace("pageName=", ""));


Content shown through client side scripting will not be SEO friendly since it is rendered on the client. But you could grab the data with a WebRequest() and parse the data on the server and send it to the client pre-rendered.


There are currently no comments, be the first to post one.

Comment Form

Only registered users may post comments.


2sic Daniel Mettler (124)
Aderson Oliveira (15)
Alec Whittington (11)
Alex Shirley (10)
Andrew Nurse (30)
Anthony Glenwright (5)
Antonio Chagoury (28)
Ash Prasad (22)
Ben Schmidt (1)
Benjamin Hermann (25)
Benoit Sarton (9)
Beth Firebaugh (12)
Bill Walker (36)
Bob Kruger (5)
Brian Dukes (2)
Brice Snow (1)
Bruce Chapman (20)
Bryan Andrews (1)
cathal connolly (55)
Charles Nurse (163)
Chris Hammond (203)
Chris Paterra (55)
Clinton Patterson (28)
Cuong Dang (21)
Daniel Bartholomew (2)
Dave Buckner (2)
David Poindexter (3)
David Rodriguez (2)
Doug Howell (11)
Erik van Ballegoij (30)
Ernst Peter Tamminga (74)
Geoff Barlow (6)
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 (270)
John Mitchell (1)
Jon Henning (14)
Jonathan Sheely (4)
Jordan Coopersmith (1)
Joseph Craig (2)
Kan Ma (1)
Keivan Beigi (3)
Ken Grierson (10)
Kevin Schreiner (6)
Leigh Pointer (31)
Lorraine Young (60)
Malik Khan (1)
Matthias Schlomann (15)
Mauricio Márquez (5)
Michael Doxsey (7)
Michael Tobisch (3)
Michael Washington (202)
Mike Horton (19)
Mitchel Sellers (28)
Nathan Rover (3)
Navin V Nagiah (14)
Néstor Sánchez (31)
Nik Kalyani (14)
Peter Donker (52)
Philip Beadle (135)
Philipp Becker (4)
Richard Dumas (22)
Robert J Collins (5)
Roger Selwyn (8)
Ruben Lopez (1)
Ryan Martinez (1)
Salar Golestanian (4)
Sanjay Mehrotra (9)
Scott McCulloch (1)
Scott S (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)
Timo Breumelhof (24)
Tony Henrich (3)
Torsten Weggen (2)
Vicenç Masanas (27)
Vincent Nguyen (3)
Vitaly Kozadayev (6)
Will Morgenweck (37)
Will Strohl (163)
William Severance (5)
Try Evoq
For Free
Start Free Trial
a Demo
See Evoq Live
Need More Information?