Application Security Testing: An Integral Part of DevOps
This is Chapter 8, "Creating a New Module," from ASP.NET Community Starter Kit by K. Scott Allen and Cristian Darie, published by Packt Publishing. For more information, go to http://www.packtpub.com/community_starter_kit/book.
Creating a New Module
The next three chapters in this book will demonstrate various methods of customizing the CSK. Every community site will have different requirements to fulfill. Although the existing CSK framework offers a great deal of flexibility, having the entire source code available means you can add additional functionality to a site in an elegant manner. In this chapter, we will concentrate on creating a new module for the CSK. We will see how creating a new module allows you to add entirely new features which integrate seamlessly with the rest of the framework. In this chapter, we will implement a Frequently Asked Questions (FAQ) module.
Before we begin, let's mention one caveat. The CSK is a living piece of software. It will undoubtedly gain additional features and modules from the developer community, so one question you may want to answer is, "Has someone else already written the module I need?" Once you've made the commitment to customizing the CSK with your own code, you'll need to also think about integrating your code into newer versions of the CSK. If you stick to the current design used by the existing modules, chances are you'll find that the upgrades are easier.
Before you begin implementing a new module for the CSK, you will first want to have a firm grasp of the features you wish to add, and then decide if any of the existing modules shipped with the CSK can fulfill that functionality.
First, let us make a brief list of requirements for our community FAQ:
- An FAQ should consist of a question, an answer, a description or introduction, and pointers to additional references.
- Community users should have the ability to comment on and rank individual FAQs, as well as offer e-mail notifications when a new FAQ appears.
- Community users should have the ability to submit a new FAQ subject for the moderator's approval.
You could certainly create a list of questions and answers marked up in HTML and add the content to a community site using the HTML Page section type. However, the HTML Page section type offers limited user interaction (no comments, ranking, e-mails, or moderation).
Alternatively, the Articles section type could provide us with what we need, if we are willing to lump the FAQ answer and reference fields together in the article's body text. For maximum flexibility in presenting information, we would prefer to keep these as distinct entities. With our requirements and direction set, let's take a look at the classes and tables we will be building.
We know from the earlier chapters that the Community_ContentPages table will keep most of the information we need for an FAQ; for example, the author name, view count, and description. If we consider the question piece of the FAQ as the title, we really only need to store the FAQ answer and additional references as attributes. We will add a database table (Community_Faqs) as shown in the following diagram:
We can then build a class to hold FAQ information. As shown in the following diagram, the FaqInfo class inherits from ContentInfo, which holds most of the attributes for any content item. Every module also uses a utility class to retrieve, add, and edit content. For the FAQs module, this is the FaqUtility class (shown without method parameters).
We will also need to build classes for the code-behind pages that display and edit FAQs. We saw in earlier chapters how pages in the CSK derive from the SkinnedCommunityControl to allow themselves to be displayed with different skins. There are also a number of base classes with most of the behavior we need to add, edit, and display FAQs. The following diagram shows the class hierarchy that we will use for the FAQ code-behind classes: We will also need to create WebControl-derived classes to display FAQ content. Typically, each attribute of our content will display in a distinct control, which allows a skin to lay out the content in whatever manner it sees fit. The following diagram shows the controls that we will use for this module, all of which ultimately derive from WebControl:
Module Construction Blueprint
We will take a bottom-up approach by starting with the database work, and finishing with presentation skins and themes. We will follow the naming conventions and patterns established by the existing modules in the CSK so that the code fits well with the rest of the framework. For example, the Books module retrieves book information from the Community_Books table to populate a BookInfo component.
Thus we will use a table called Community_Faqs to populate an FaqInfo component. However, you may want to consider adding a unique identifier in case a future version of the CSK contains the module you are building. For instance, if you work for ABC Inc. you might use Community_ABCFaqs as a table name to lower the possibility of future name collisions.
We will use the following steps to build the FAQ module. You can also follow these steps in a general sense to build your own modules:
- Create a new table (Community_Faqs) to hold the additional fields for the new module.
- Create stored procedures to add, edit, and select a single FAQ, and a stored procedure to select all FAQs for a given section.
- Create a maintenance stored procedure to initialize the FAQ module by populating the Community_PageTypes and Community_NamedPages tables.
- Create a component (FaqInfo) to hold information on a single FAQ.
- Create a component (FaqUtility) with data-access routines to invoke our FAQ-related stored procedures.
- Create controls derived from WebControl to display the individual module fields. These controls will be FaqQuestion, FaqIntro, FaqAnswer, FaqReference, and FaqEditContent.
- Create controls derived from the SkinnedCommunityControl class to contain the logic behind the page content skins from the next step. These controls will be AddFaq, EditFaq, FaqSection, and Faq.
- Create new page-content skins for the module. This includes Faqs_AddFaq.ascx, Faqs_FaqSection.ascx, and Faq_Faq.ascx. We will use the Faqs_AddFaq skin to add a new FAQ as well as edit an existing FAQ. At a minimum you will need to create a default skin in the ContentSkins folder under the Communities\Common\Themes\Default\Skins directory. You can optionally create additional skins for other available themes, such as the Robotico and Professional themes.
- Create style rules in the CSS files in Communities\Common\Themes\Default\Styles for the module. You should also add CSS rules into all of the CSS files in the themes your community may use.
The rest of this chapter will explain each step in more detail.
The Community_Faq Table
Content that is common to all modules such as title, description, and view count resides in the Community_ContentPages table. Additional module-specific content requires a second table for storage. For the FAQ module, we can store the FAQ question in the contentPage_title field of Community_ContentPages, and the FAQ introduction in the contentPage_description field. We still need to store the FAQ answer and the additional references text for the FAQ, so we will use the following DDL to create a table:
CREATE TABLE [Community_Faqs] ( [Faq_ContentPageID] [int] NOT NULL , [Faq_Answer] [ntext] NOT NULL , [Faq_Reference] [ntext] NULL, CONSTRAINT [PK_Community_Faqs] PRIMARY KEY CLUSTERED ( [Faq_ContentPageID] ), CONSTRAINT [FK_Community_Faqs_Community_ContentPages] FOREIGN KEY ( [Faq_ContentPageID] ) REFERENCES [Community_ContentPages] ( [contentPage_id] ) ON DELETE CASCADE )
The naming conventions and data types we use for the table are consistent with the other modules shipped with the CSK.
We store the answer and reference data in fields of type ntext in order to support large quantities of text (up to 1 GB). Also notice how Faq_Answer is a required field but Faq_Reference may contain a NULL value. Our primary key (Faq_ContentPageID) references the additional FAQ content in the Community_ContentPages table. The foreign key constraint will ensure our referential integrity for this relationship.
Another nice design feature is the use of cascading referential integrity restraints. The ON DELETE CASCADE clause in the foreign key constraint means we will not need to write any code to delete an FAQ record from Community_Faqs.
The CSK uses the Community_ContentPagesDeleteContentPage stored procedure to delete records from the Community_ContentPages table. When the procedure removes a record from the content table, SQL Server will automatically remove the corresponding entry from the FAQ table.
The Community_Faqs Stored Procedures
The next steps in our blueprint calls for us to create stored procedures to add an FAQ, edit an FAQ, retrieve a single FAQ, and retrieve a paged and sorted list of FAQs, for a total of four procedures. All of the data access in the CSK happens via stored procedures. There is no ad hoc SQL in the code, which is a good practice from both encapsulation and security standpoints. The first procedure we will write is to add new FAQ content.