logo

Premium Support
24/7/365

Premium support customers may submit help-desk tickets using the form below.
Alternatively, the help-desk system can be accessed directly at support.maxiomtech.com. support@maxiomtech.com
info@maxiomtech.com
+1 (703) 942-9420
 

Using DotNetNuke Service Framework for Cross Site Content Sharing

Using DotNetNuke Service Framework for Cross Site Content Sharing

In 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

You 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.

PageSharingRouteMapper.cs

using DotNetNuke.Web.Api;
namespace TwentyTech.PageSharing
{
    public class PageSharingRouteMapper : IServiceRouteMapper
    {
        public void RegisterRoutes(IMapRoute mapRouteManager)
        {
            mapRouteManager.MapHttpRoute("InspectorIT/PageSharing", "default",
                "{controller}/{action}", new[] { "InspectorIT.PageSharing" });
        }
    }
}

PageSharingController.cs

[AllowAnonymous]
public class PageSharingController : DnnApiController
{
    [HttpPost]
    public HttpResponseMessage GetRecentPages()
    {
        try
        {
            List<SimplePageInfo> pages =
                TabController.GetPortalTabs(Globals.GetPortalSettings().PortalId, -1, false, false)
                .Select(page => new SimplePageInfo(page)).ToList();

            return Request.CreateResponse(HttpStatusCode.OK,pages);
        }
        catch(Exception ex)
        {
            Exceptions.LogException(ex);
            return Request.CreateResponse(HttpStatusCode.OK, "error");
        }
    }
}
[AllowAnonymous] attribute is important because it will allows unauthenticated users to be able to access the functions within the controller.

SimplePageInfo.cs

public class SimplePageInfo
{
    public SimplePageInfo(TabInfo page)
    {
        Fill(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);
        if(user==null)
        {
            CreatedByUser = "Unknown";
        }else
        {
            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:

<add name="Access-Control-Allow-Origin" value="*" />
<add name="Access-Control-Allow-Origin" value="http://sub.inspectorit.com" />

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>
</ul><script type="text/javascript">
    $(document).ready(function () {
       var pageUrl = "http://pagesharing.loc/DesktopModules/InspectorIT/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)
                    };
                    ko.applyBindings(viewModel);
                };
                xdr.open("POST", pageUrl);
                xdr.send();
            }
            else {
                $.ajax({
                    type: "POST",
                    url: pageUrl,
                }).done(function (pages) {
                    if ($.browser.mozilla) {
                        pages = JSON.parse(pages);
                    }

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

        };

        GetRecentPages();
    });
</script>

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.

Content shown through client side scripting will likely 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.

Jonathan Sheely

Sr Software Engineer at InspectorIT. Jonathan is an out of the box thinker who has over 10 years experience building and supporting web application software and infrastructure. Jon specializes in ASP.NET C#, Javascript and CSS but is willing to learn anything that gets the job done.

4 Comments
  • Posted at 3:04 am, April 27, 2013

    Hi Jonathan, thanks for the article – I am battling to implement CORS in my DNN webapi – basically I need to expose a Service that can be called from a mobile app – e.g. Cross Domain. Am I the only person doing this or am I doing it wrong? How can this be done cleanly in DNN? thanks

  • Posted at 5:35 am, May 15, 2013
  • Posted at 3:45 pm, July 9, 2013

    Hi Jonathan,
    Ran into your article on Google.. Found some missing pieces for the new search module I’m writing here using this service framework. Thanks!
    Feray