New Community Website

Ordinarily, you'd be at the right spot, but we've recently launched a brand new community website... For the community, by the community.

Yay... Take Me to the Community!

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.

Adding Core Taxonomy to Your Module: Part 2 – Content Items

If you have not done so already, please review the first part of this blog series: Part 1 – Getting Started. Part 1 provides information which is very important to understand prior to getting into the actual module development, which this part (and all future parts) of the series do. In this part, as the title suggests, we will be covering Content Items. One thing worth mentioning before we dive in here is that as you read this blog entry, pieces of code/variables may not be 100% clear until after you have read this part as well as Part 3 and Part 4. The reason for this is because my sample module (and based on my recommendations, your own module) was already complete and THEN I integrated taxonomy. The approach I took for writing this blog series is the order in which I tackled the integration and one that seemed the most logical to me (and the same approach, for the most part, I would take again in any future integrations). That said, let’s move on to the first section, Content Types.

Content Types

In Part 2 of Charles blog he touched on Content and its data structure which consists of two tables: ContentItems and ContentTypes (there are actually 2 other tables, ContentItems_MetaData and ContentItems_Tags, but these are not important for now). As a module developer who is integrating Core taxonomy, one of the very first things you are going to have to do is create your own Content Type. Before we dive into details about how to do that, let’s go over a few things to consider:

  1. You Content Type name must be unique and you have 100 characters to create the name. My advice is to follow module naming conventions and possibly add the entity name (Entities are discussed in Part 3) to avoid potential name duplication.
  2. A module should only contain a single Content Type (Perhaps there is a use case for more but I can’t really think of a good reason to have more than one).
  3. Utilize the core API and avoid creating your own methods/stored procedures for creating/retrieving/updating.
  4. My advice is to do this content type check any time you are attempting to create a content item (we will be touching on this soon).

Sample Content Type Check:

   1: var typeController = new ContentTypeController();
   2: var colContentTypes = (from t in typeController.GetContentTypes() where t.ContentType == "MyCompanyMyModule" select t);
   3: var contentTypeID = 0;
   5: if (colContentTypes.Count() > 0)
   6: {
   7:     var contentType = colContentTypes.Single();
   8:     contentTypeID = contentType == null ? CreateContentType() : contentType.ContentTypeId;
   9: }
  10: else
  11: {
  12:     contentTypeID = CreateContentType();
  13: }

In line 1 we aren’t doing anything ground breaking here, we are simply creating a new instance of the ContentTypeController (which is under the DotNetNuke.Entities.Content namespace). In line 2 we are using Linq to determine if the content type we are using in our module exists already in the data store. For this, you will need to add a reference to System.Linq in your project (if you don’t have one already). In line 5 we are checking to see if line 2 returned a valid result for our content type. If it did return a valid result, we are assigning the content type’s primary key (ContentTypeId) to our variable in line 8 (NOTE: It also will call CreateContentType() if there somehow ended up being a problem retrieving our Content Type). If it did NOT return a valid result, we call a function that will create it for us (in line 12) that returns the new primary key (and thus populates our contentTypeID variable).

Sample Content Type Creation:

   1: private static int CreateContentType() {
   2:     var typeController = new ContentTypeController();
   3:     var objContentType = new ContentType { ContentType = "MyCompanyMyModule" };
   5:     return typeController.AddContentType(objContentType);
   6: }

Because of the way I broke out my code, I am required to create another instance of the ContentTypeController in line 2. In line 3 we are creating a new ContentType object and assigning the only property we should set, the name, and assigning it to our objContentType variable (Note: you should probably create a constant string to use for your ContentTypeName here and utilize it in the previous two snippets). In line 5 we are using the core API to create our new Content Type by passing it our objContentType variable from line 3 and returning the primary key value returned from the data store.

Content Items

At this point we have a valid Content Type for our module and it is time to move onto Content Item creation. As I mentioned previously in Part 1 of this series, the sample I am doing here is around a module that will have multiple Content Items per module instance. Because of this, in my project I created a class which contains methods that can easily be called repeatedly to avoid code duplication (I used a file named MyModule\Components\Taxonomy\Content.cs). This is where I placed all code covered in this part of the blog series. Since we now know where this code is located, lets cover what the code for Content Item creation and updating should be and what it does (we will cover when to call it in Part 4). Before doing that, however, let’s cover a couple of the columns located in the ContentItems table (outlined in Charles Part 2 blog entry) for a little more clarity in relation to integration development:

  1. Content: This is going to be the actual content of your content entity. In something like a simple article module, this will be the article’s body. In a forum thread, this will be the content of the first post (although, there is a case for all posts in a thread here too).
    1. Honestly, I am not sure why we duplicate content here that is also stored in other tables. This may have something to do with future search or MetaData integration or for other reasons. Perhaps Charles or someone else can explain better.
  2. ContentKey: This is used, in combination with TabID, when a tag is clicked in the user interface and tag search results are returned. Basically, this needs to consist of several items that will allow users to view your content item directly based on links provided from the tag search results.

Each time you create a new piece of content you wish to allow taxonomy integration with, you should also create a valid Content Item in DotNetNuke (if you are retro-fitting a deployed module, I touch on other concerns in Part 4). When doing this, I found it to be best done AFTER my entity is in the data store (and thus we have a primary key, Ex: ArticleId) but the concept of when will be covered more in Part 4. In addition to the two columns discussed above, when creating a Content Item you should also provide it a TabID, a ModuleID, set Indexed = false (for now, since there isn’t search integration yet) and you must set the ContentTypeID. Assuming you have a ContentTypeId generated from the code snippets above (and you have assigned it to the contentTypeID variable), your Create Content Item code is going to look similar to the following:

Sample Create Content Item:

   1: var objContent = new ContentItem
   2:                      {
   3:                          Content = objEntry.MyContent,
   4:                          ContentTypeId = contentTypeID,
   5:                          Indexed = false,
   6:                          ContentKey = "mid=" +  objEntry.ModuleId + "&ctl=DetailControlKey" + "&EntryID=" + objEntry.EntryId,
   7:                          ModuleID = objEntry.ModuleId,
   8:                          TabID = tabId
   9:                      };
  11: objContent.ContentItemId = Util.GetContentController().AddContentItem(objContent);

In line 1 I am initializing a new ContenItem that I am assigning to my objContent variable (which I use in line 11). In lines 3, 6 & 7 you can see I am using a variable objEntry. This is simply a strongly typed entity in our module that I would have passed into my CreateContentItem function (along with tabId). Also in line 3, I am setting the value of Content equal to the content of my entity. Line 4 is where I set the very important ContentTypeId which I retrieved via my previous code snippets. Line 5 should be self-explanatory. Line 6, where we set the ContentKey, you should make note of the format here. This is one that works when FriendlyURL’s are used (I have not attempted to use it with them off but I believe it should be handled properly). Lines should also be self-explanatory. Line 8 is, as it says, TabID. The reason I wanted to point this out, although it is probably self-explanatory, is that this is very important to set for our tagging search results. If we do not set it properly (in combination with ContentKey) our tagging search results will not return proper links to view an item in detail. The final line, line 11, is where we use the Core API to actually create our new Content Item by calling Util.GetContentController().AddContentItem (which is under the DotNetNuke.Entities.Content.Common namespace). As you can see, we are passing it our ContentItem, objContent, and are assigning the returned primary key value from the data store to our objContent’s ContentItemId. This is very important because we will need this later (also covered in Part 4).

By now, you should have an understanding of how to create a Content Item and what is required. With that in mind, updating a Content Item is pretty simple. The updating of Content Items should occur when the content associated with it is updated (again, when is covered in Part 4). To do this, you simply need code that looks similar to the following:

Sample Update Content Item:

   1: public void UpdateContentItem(EntryInfo objEntry, int tabId) {
   2:     var objContent = Util.GetContentController().GetContentItem(objEntry.ContentItemId);
   4:     if (objContent == null) return;
   5:     objContent.Content = objEntry.MyContent;
   6:     objContent.TabID = tabId;
   7:     Util.GetContentController().UpdateContentItem(objContent);
   8: }

In line 1 you can see our void/sub method is utilizing the objEntry variable and also receiving tabId (just like the CreateContentItem function). In line 2, we are attempting to retrieve our Content Item via the Core API using the GetContentItem and we are passing it the ContentItemId (the same one returned in line 11 of the sample create content item code snippet). Because we need this ContentItemId, as you have probably guessed, we are going to need to store it in our module’s table in a new column (yet again, another thing we will cover in Part 4 but Part 3 will also touch on this as well). Line 4 should be self-explanatory. Line 5, we are updating the value of our ContentItem’s content. Line 6, we are updating the value of the TabID just in case our module moved pages. Although I don’t really touch on it in this blog series, you should always assign this here in case the module is moved from one page to another. At this time, I not certain exactly how that scenario (when a module moves pages) should be handled for updating associated Content Items which is why I do not address it in this blog series, now back to our sample. Line 7 is where we use the Core API to Update the Content Item. This method updates the ContentItems table in the data store for us. You could easily expand this method to include other properties/columns if you deem it necessary in your own implementation.

Well, that pretty much sums up Content Items and how you can create and update them (as well as Content Types). In Part 3, I will be covering Entities and how they relate to taxonomy.


Joseph Allen
Nice post, but there are broken links in the text.
Joseph Allen Thursday, October 10, 2013 8:39 PM (link)
Joseph Allen
Instead of running a check for Content Type every time a content item is added, why not just add a SQL during the module installation or upgrade to add the content type?

Adding the content type once during installation would mean not having to run the check scores or thousands of times (one for every new content item).
Joseph Allen Wednesday, December 18, 2013 10:52 AM (link)
cathal connolly
@Joseph -that's a valid approach. Another is to use IUpgradeable and add it that way (we tend to do this in the platform - this blog is 3 years old so somewhat predates common usage)
cathal connolly Wednesday, December 18, 2013 11:08 AM (link)
Joseph Allen
@ Cathal - Are there any thoughts on doing an updated series on Taxonomy/Folksonomy and ContentItem or adding a more robust wiki topic? This series is a little hard follow without a "Hello World" project and source code.

Having a schema (as it relates to a "Hello World Module") would be very helpful.
Joseph Allen Wednesday, December 18, 2013 11:24 AM (link)
Duncan Ion
I agree with Joseph,
The example code in this series appears to be out of date in that the referenced libraries are no longer used.
Duncan Ion Friday, March 14, 2014 6:13 PM (link)

Comment Form

Only registered users may post comments.


Aderson Oliveira (22)
Alec Whittington (11)
Alessandra Davies (3)
Alex Shirley (10)
Andrew Hoefling (3)
Andrew Nurse (30)
Andy Tryba (1)
Anthony Glenwright (5)
Antonio Chagoury (28)
Ash Prasad (37)
Ben Schmidt (1)
Benjamin Hermann (25)
Benoit Sarton (9)
Beth Firebaugh (12)
Bill Walker (36)
Bob Kruger (5)
Bogdan Litescu (1)
Brian Dukes (2)
Brice Snow (1)
Bruce Chapman (20)
Bryan Andrews (1)
cathal connolly (55)
Charles Nurse (163)
Chris Hammond (213)
Chris Paterra (55)
Clint Patterson (108)
Cuong Dang (21)
Daniel Bartholomew (2)
Daniel Mettler (181)
Daniel Valadas (48)
Dave Buckner (2)
David Poindexter (12)
David Rodriguez (3)
Dennis Shiao (1)
Doug Howell (11)
Erik van Ballegoij (30)
Ernst Peter Tamminga (80)
Francisco Perez Andres (17)
Geoff Barlow (12)
George Alatrash (12)
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 (274)
John Mitchell (1)
Jon Henning (14)
Jonathan Sheely (4)
Jordan Coopersmith (1)
Joseph Craig (2)
Kan Ma (1)
Keivan Beigi (3)
Kelly Ford (4)
Ken Grierson (10)
Kevin Schreiner (6)
Leigh Pointer (31)
Lorraine Young (60)
Malik Khan (1)
Matt Rutledge (2)
Matthias Schlomann (16)
Mauricio Márquez (5)
Michael Doxsey (7)
Michael Tobisch (3)
Michael Washington (202)
Miguel Gatmaytan (3)
Mike Horton (19)
Mitchel Sellers (40)
Nathan Rover (3)
Navin V Nagiah (14)
Néstor Sánchez (31)
Nik Kalyani (14)
Oliver Hine (1)
Patricio F. Salinas (1)
Patrick Ryan (1)
Peter Donker (54)
Philip Beadle (135)
Philipp Becker (4)
Richard Dumas (22)
Robert J Collins (5)
Roger Selwyn (8)
Ruben Lopez (1)
Ryan Martinez (1)
Sacha Trauwaen (1)
Salar Golestanian (4)
Sanjay Mehrotra (9)
Scott McCulloch (1)
Scott Schlesier (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)
Steven Fisher (1)
Timo Breumelhof (24)
Tony Henrich (3)
Torsten Weggen (3)
Tycho de Waard (4)
Vicenç Masanas (27)
Vincent Nguyen (3)
Vitaly Kozadayev (6)
Will Morgenweck (40)
Will Strohl (180)
William Severance (5)
What is Liquid Content?
Find Out
What is Liquid Content?
Find Out
What is Liquid Content?
Find Out