Real - World ASP .NET—Building a Content Management System - StephenR. G. Fraser
.pdf
Figure 12-8: The AdmAcntView Web page
Summary
This chapter covered cookies, authentication, authorization, and encryption.
It started with a brief discussion of ASP.NET's security and then moved on to cover CMS.NET's security in a little more detail. Then it covered cookies and session objects and how to build and use them. Next, it described the method by which ASP.NET provides authentication and then went into detail on how CMS.NET does authentication. After this, it moved on to ASP.NET authorization and CMS.NET role-based authorization and why it was decided to not use ASP.NET authentication at all. This chapter briefly explored encryption and then ended by covering account management.
In the next chapter, you start to have some real fun—you get to start displaying content you have accumulated to the world.
Chapter 13: Displaying Dynamic Content
Overview
I've had enough of back-end administrative development, how about you? All the previous chapters were fun, but there's nothing like being able to display dynamic content and show off all your hard work. The average user will never see all the work you have put into the system so far. Not so with this chapter, however, because dynamic content display is all about strutting your stuff in front of users.
You will just use a standard display template for all users visiting the site. Believe it or not, even the simple display template provides a lot to the user. But, there is nothing stopping you from creating a much more elaborate template in your implementation of CMS.NET.
CMS.NET provides a home page, a way of dividing the content into as many domains and subdomains as you think necessary, and the capability to navigate in and out of all these domains, zones, and the home page. The content is divided into a header, source, teaser, body, and tagline, of which only the header and body are mandatory. As an added bonus, you have full HTML formatting capabilities within all the aforementioned sections.
Not too bad for one chapter.
But that's not all ... (I've always wanted to say that!) I'll throw in a content deployment system for free.
What Is Dynamic Content?
If you are like everyone else, your first attempts at building a Web site started with a tool such as Microsoft FrontPage. The first thought you probably had was "This isn't so hard." You created page after page. Soon, you began to realize all the pages were starting to look alike, except for this or that section of each page. After a while, the sheer number of pages you had to maintain became mind-boggling and you started to think "There has to be a better way." Fortunately, there is. It is called dynamic content.
As a concept, dynamic content is very easy. It is simply content created at the time it is needed rather than in advance. Basically, dynamic content is achieved by storing all the pieces of content that a user might want to view and then, when the user requests some
of this content, a Web page—specific to the request of the user—is built.
Dynamic content sounds easy enough, but until the writing this book, it cost hundreds of thousands of dollars to buy a system to implement. Yes, these systems provide many bells and whistles, but I ask you, hundreds of thousands of dollars worth?
Creating a static Web site using only HTML is really a thing of the past. Even the pages you create with FrontPage enable you to throw in dynamic components and controls.
In this chapter, the designer of CMS.NET defines the dynamic nature of the Web site. That designer is, of course, yours truly. When you combine dynamic content with personalization, the design enables the user to start taking some control of the dynamic nature of the site. You will see that you can achieve a truly unique experience for each user.
Three-Level Content Navigation
CMS.NET uses the very common three-level approach to content navigation. Take a quick peek around the Internet. You will find that it usually takes about three or four levels before you get to the story you are looking for on a given Web site. Most people do not want to go much deeper than four levels into a Web site, and most Web sites hover around three levels. Personalized sites will often shave one level off your navigation.
Why Three-Level Navigation?
It seems to be an unwritten industry standard. Having three levels just feels right. I find that two-level sites require a lot of scrolling to find what you are looking for. On the other hand, with a Web site that is four levels deep or more, you start to get the feeling of "Will I ever get there?"
CMS.NET doesn't force three levels, though. If you that find the story you want pops up while navigating, there is always a quick hyperlink to it. But, of course, this is only common sense.
So What Are the Three Levels of CMS.NET?
The top level is made up of one or more subject area domains. They can be almost any high-level abstraction of your content system. As you can see in Figure 13-1, this book's implementation of CMS.NET uses Developer Content and Management Content as domains, but any high-level abstraction could be used.
Figure 13-1: Developer Content domain zones
Inside each domain are subdomains, or what CMS.NET calls zones. Again, they could be almost any abstraction. As you can see in Figures 13-1 and 13-2, the domains are broken up as follows:
§Developer Content
o |
e-Business |
o |
Development |
o |
Database |
oBusiness Solutions
§Management Content
o |
Technology |
o |
Professional |
o |
Managerial |
Figure 13-2: Management Content domain zones
The third and final level of CMS.NE T is content grouped by story. The same piece of content can be used in different zones if it makes sense to do so. For example, if a story is about implementing a database in an e-business solution, it makes sense to include the story in both zones. As you can see in Figure 13-3, the data used is just test data, but obviously, you are going to want to implement your real content.
Figure 13-3: Managed C++ book story
Starting and Stopping the Web Site
There is no greater sin made by a Webmaster than allowing the "The page cannot be displayed" error (see Figure 13-4) to show up during a user's travels through the site. It is the kiss of death if it shows up just as someone is trying to access your Web site for the first time. Think of it from the perspective of the user.
Figure 13-4: "The page cannot be found" error
The first rule to remember about users is that they think there are probably a gazillion other Web sites on the Internet with content similar to yours. If yours isn't available, the user will go elsewhere. Only by showing the user that your site is the best thing since sliced bread will he likely return.
If you can't have your site up for some reason (and it had better be a good one), make sure you provide a Web page that tells the user that he got the right place and to please come back later. Heck, you might get lucky and the user will try back later. If the user
gets the "The page cannot be found" error page, however, the chances are nil to none that you will see either hide or hair of that user again.
There really is no excuse for the "The page cannot be found" error to show up. It is such an easy task to put up a dummy page (if the Web server is running) or to route the IP address to somewhere else while the Web server is being restarted. Figure 13-5 shows CMS.NET's friendly "I'm really here but come back later" page.
Figure 13-5: The "I'm changing" page
The Default.aspx Web Page
Chapter 10 covered all the code needed to handle shutting down and starting up the system. This time, instead of the Admin.aspx Web page checking for the setup
<appSettings> element, the Default.aspx Web site is looking for the ready
<appSettings> element. If Default.aspx finds the ready <appSettings> element value set to true in the web.config file, it knows that the system is running and can continue as normal. On the other hand, if the value returned is false or does not exist, the Web site knows it is shut down and, instead of redirecting to the home page, displays it own message asking the user to come back later. Listing 13-1 shows the Default.aspx Codebehind to handle this process.
Listing 13-1: The Default.aspx Codebehind
private void Page_Load(object sender, System.EventArgs e)
{
string ready = new AppEnv(Context).GetAppSetting("ready");
if(ready.Equals("true"))
{
Response.Redirect("CDA/HomePg.aspx");
}
}
The AdmShutdown Web Page
To give the Webmaster even less of an excuse, CMS.NET made the process of bringing the Web site up and down extremely easy. Whenever maintenance needs to be done on CMS.NET that does not require the Web server to be shut down, the Webmaster simply has to navigate to the AdmShutdown Web page and press the single button found on it. On the other hand, if you need to bring down the Web server for some reason along with the Web site, you need to reroute your host name to a new IP address. The procedure to do this differs with each host provider, but it should simply be, in most cases, a phone call telling the host provider of your intentions.
As you can see in Figure 13-6, the Web page to shut down and start the system consists of merely one heading, label, and button. If you can't create the ASP.NET design for this, you shouldn't be reading this book yet.
Figure 13-6: The AdmShutdown Web page
The AdmShutdown Codebehind
The Page_Load() method (see Listing 13-2) is a very simple procedure to dynamically place the appropriate label on the prompt and button. First, it gets the value of the ready
<appSettings> element. Then, if the value of ready is true, meaning the site is up, it prompts the Webmaster to shut the Web site down. On the other hand, if the value is anything but true, it prompts the Webmaster to bring the Web site up.
Listing 13-2: The AdmShutdown Page_Load Method
private void Page_Load(object sender, System.EventArgs e)
{
ready = appEnv.GetAppSetting("ready");
if (ready.Equals("true"))
{
lbPrompt.Text = "The site is currently up.";
bnStartStop.Text = "Bring site down?";
}
else
{
lbPrompt.Text = "The site is currently down.";
bnStartStop.Text = "Bring site up?";
}
}
The bnStartStop_Click() method (see Listing 13-3) is a little more elaborate, but you already have covered all the code in Chapter 10. All it does is simply toggle the ready <appSettings> element in the web.config file from true to false and back.
The first time the Web site is ever started up, the ready <appSettings> element is added to the web.config file. Finally, the prompt and button's text gets toggled to its opposite state.
Listing 13-3: The AdmShutdown bnStartStop_Click Method
private void bnStartStop_Click(object sender, System.EventArgs e)
{
bool isready = false;
XmlReader xtr =
new XmlTextReader(File.OpenRead(Server.MapPath("..\\..\\web.config")));
XmlDocument doc = new XmlDocument();
doc.Load(xtr);
xtr.Close();
XmlNodeList nodes =
doc.DocumentElement.GetElementsByTagName("appSettings");
for (int i = 0; i < nodes.Count; i++)
{
XmlNodeList appnodes = ((XmlElement)(nodes.Item(i))).GetElementsByTagName("add");
for (int j = 0; j < appnodes.Count; j++)
{
XmlAttributeCollection attrColl = appnodes.Item(j).Attributes; XmlAttribute tmpNode = (XmlAttribute)attrColl.GetNamedItem("key"); if (tmpNode.Value.Equals("ready"))
{
if (ready.Equals("true")) ((XmlAttribute)attrColl.GetNamedItem("value")).Value =
"false";
else ((XmlAttribute)attrColl.GetNamedItem("value")).Value =
"true"; isready = true;
}
}
if (!isready)
{
// if it gets here, it's the first time the site is started up XmlDocumentFragment newAppSetting = doc.CreateDocumentFragment();
newAppSetting.InnerXml=("<add key=\"ready\" value=\"true\" />\n"); ((XmlElement)(nodes.Item(i))).AppendChild(newAppSetting);
}
}
File.Delete(Server.MapPath("..\\..\\web.config"));
StreamWriter sr =
new StreamWriter(File.OpenWrite(Server.MapPath("..\\..\\web.config"))); doc.Save(sr);
sr.Close();
// Flip prompt
if (ready.Equals("true"))
{
lbPrompt.Text = "The site is currently down."; bnStartStop.Text = "Bring site up?";
}
else
{
lbPrompt.Text = "The site is currently up."; bnStartStop.Text = "Bring site down?";
}
}
Navigational Database Tables
As you might think, CMS.NET's three-level navigation requires three additional database tables to be added to the repository: Domain, Zone, and Distribution. All three correspond directly to the three-level navigational scheme of CMS.NET.
The Domain database table, as the name suggests, stores all the domain information for CMS.NET. As you can see in Table 13-1, there is not much to the table. The table is primarily used to dynamically build CMS.NET's NavBar. The DomainTitle is displayed on the NavBar, whereas the DomainType is used in building the NavBar hyperlinks.
|
Table 13-1: Domain Database Table Design |
|
|
|
|
|
|
||||
|
|
|
|
|
|
|
|
|
|
|
|
|
COLUMN NAME |
|
DATA |
|
|
LENGTH |
|
KEY |
|
DESCRIPTION |
|
|
|
|
TYPE |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Table 13-1: Domain Database Table Design |
|
|
|
|
|
|
||||
|
|
|
|
|
|
|
|
|
|
|
|
|
COLUMN NAME |
|
DATA |
|
|
LENGTH |
|
KEY |
|
DESCRIPTION |
|
|
|
|
TYPE |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
DomainID |
|
int |
|
4 |
|
true |
|
Domain ID for |
|
|
|
|
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
this domain |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
DomainType |
|
char |
|
32 |
|
false |
|
Type of Web |
|
|
|
|
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
page |
|
|
|
|
|
|
|
|
|
|
|
associated |
|
|
|
|
|
|
|
|
|
|
|
with this |
|
|
|
|
|
|
|
|
|
|
|
domain |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Title |
|
char |
|
32 |
|
false |
|
Title of the |
|
|
|
|
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
domain |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Description |
|
char |
|
64 |
|
false |
|
Brief |
|
|
|
|
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
description of |
|
|
|
|
|
|
|
|
|
|
|
the domain |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ModifiedDate |
|
datetime |
|
8 |
|
false |
|
Date domain |
|
|
|
|
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
was last |
|
|
|
|
|
|
|
|
|
|
|
changed |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CreationDate |
|
datetime |
|
8 |
|
false |
|
Date domain |
|
|
|
|
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
was created |
|
|
|
|
|
|
|
|
|
|
|
|
|
Figure 13-7 shows the contents of the Domain database used by CMS.NET. Each domain gets its own unique DomainID, which is used to create a one-to-many relationship with Zone. The NavBar uses DomainType to determine what type of Web page to display when a hyperlink is selected from it. In fact, we will be creating homepg.aspx and contentpg.aspx Web pages a little later on.
Figure 13-7: Domain database for CMS.NET
The Zone database table stores all the zone information of the Web site. As you can see in Table 13-2, this table is no more difficult than the Domain table. ContentPg.aspx primarily uses this table. This page's role is to display the lead story for each zone within a domain.
|
Table 13-2: Zone Database Table Design |
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
|
|
COLUMN NAME |
|
DATA |
|
LENGTH |
|
KEY |
|
DESCRIPTION |
|
|
|
|
TYPE |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ZoneID |
|
int |
|
4 |
|
true |
|
Zone ID for |
|
|
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
this domain |
|
|
|
|
|
|
|
|
|
|
|
|
|
Title |
|
char |
|
32 |
|
false |
|
Title of the |
|
|
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
zone |
|
|
|
|
|
|
|
|
|
|
|
|
|
Description |
|
char |
|
64 |
|
false |
|
Brief |
|
|
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
description of |
|
|
|
|
|
|
|
|
|
|
the zone |
|
|
|
|
|
|
|
|
|
|
|
|
|
DomainID |
|
int |
|
4 |
|
false |
|
The domain |
|
|
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
with which this |
|
|
|
|
|
|
|
|
|
|
zone is |
|
|
|
|
|
|
|
|
|
|
associated |
|
|
|
|
|
|
|
|
|
|
|
|
|
ModifiedDate |
|
datetime |
|
8 |
|
false |
|
Date zone was |
|
|
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
last changed |
|
|
|
|
|
|
|
|
|
|
|
|
|
CreationDate |
|
datetime |
|
8 |
|
false |
|
Date zone was |
|
|
|
|
|
|
|
|
|
|
|
|
|
Table 13-2: Zone Database Table Design |
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
|
|
COLUMN NAME |
|
DATA |
|
LENGTH |
|
KEY |
|
DESCRIPTION |
|
|
|
|
TYPE |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
created |
|
|
|
|
|
|
|
|
|
|
|
|
Figure 13-8 shows the Zone database used by CMS.NET. Like the Domain database, each zone gets its own ZoneID, which is used by the Distribution database to distribute stories. The DomainID is used to create a relationship with a domain. A zone can only have a relationship with a single domain.
Figure 13-8: Zone database for CMS.NET
It is possible to simplify CMS.NET by restricting a piece of content to only a certain zone. However, this restriction would be a mistake because many pieces of content can and should be associated with many zones. Guess what this leads to? It leads to the dreaded many-to-many relationship, and as any database developer will tell you, you are going to need an intersection entity to resolve this type of relationship.
The Distribution database table (see Table 13-3) is this intersection entity. The database table contains a list of every zone with which each piece of content is associated. The table also has a ranking or weighting factor that determines the importance of a piece of content to a particular zone. You might notice that because this ranking is in the intersection entity, you are able to have different weightings for the same piece of content in different zones.
|
Table 13-3: Distribution Database Table Design |
|
|
|
|
|
||||
|
|
|
|
|
|
|
|
|
|
|
|
COLUMN NAME |
|
DATA |
|
LENGTH |
|
KEY |
|
DESCRIPTION |
|
|
|
|
TYPE |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ContentID |
|
int |
|
4 |
|
true |
|
Content ID for this |
|
|
|
|
|
|
|
|
|
ZoneID |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Version |
|
int |
|
4 |
|
true |
|
Version for this |
|
|
|
|
|
|
|
|
|
ZoneID |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ZoneID |
|
int |
|
4 |
|
true |
|
Zone ID for this |
|
|
|
|
|
|
|
|
|
ContentID/Vers |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ion pair |
|
|
|
|
|
|
|
|
|
|
|
|
|
Ranking |
|
int |
|
4 |
|
false |
|
The ranking of this |
|
|
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
content for this |
|
|
|
|
|
|
|
|
|
|
zone |
|
|
|
|
|
|
|
|
|
|
|
|
|
ModifiedDate |
|
datetime |
|
8 |
|
false |
|
Date distribution |
|
|
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
was last changed |
|
|
|
|
|
|
|
|
|
|
|
|
|
CreationDate |
|
datetime |
|
8 |
|
false |
|
Date distribution |
|
|
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
was created |
|
|
|
|
|
|
|
|
|
|
|
|
Figure 13-9 shows the intersection data found in the Distribution database. Notice that a story with the same ContentID and Version can be related to multiple ZoneIDs. The Ranking column is used to sort content within a zone. Sorting will be done in descending order. Thus, a story with a ranking of 4 will be displayed before one with a ranking of 3.
Figure 13-9: Distribution database for CMS.NET
