Real - World ASP .NET—Building a Content Management System - StephenR. G. Fraser
.pdf
Only the Send() method actually does the work of sending the e-mail. Basically, the Send() method populates the e-mail with a subject line based on the alert code passed to it. Then, it checks to see if the e-mail is addressed to a specific person. If so, it addresses the e-mail only to him. If it is addressed to user zero, this means it should be sent to all users of a role specified by the alert code found. Th e process of getting all the accounts of a specified role is done by a simple select of the AccountRoles database table.
If you were observant you may have noticed the new AppSetting: smtpserver. When this value in empty, no e-mail notification is sent. The population of this AppSetting was added to setup/setup2.aspx. Basically, the change was adding a new text field and then making sure that the AppSetting is set along with all the others. The code involved is simply a cut and paste of any of the other AppSettings on the Web page.
To implement the alert, simply add two lines to the Web page where you want the alert sent, one to create the EmailAlert and the second to send it.
EmailAlert ea = new EmailAlert(Context, code, user);
ea.Send();
In the case of CMS.NET, you add this code to the submittal, approval, and return content Web pages. All of these pages are state transition Web pages because, once executed, the user no longer controls the content.
The Authoring Phase
Chapter 11 covered the author's process, but then there was only one user, the administrator, and roles had not been covered. A major problem with the earlier implementation of the author process was that the author had the right to access all content. When you later added more users and roles, the author was able to access content that was not even his own.
This may not seem too bad until you sit down and think of the ramifications, the worst of which is the complete lack of coordination in the development of content because many authors could work on the same piece of content at the same time without being aware of it. It is very dangerous to allow this in a CMS without careful monitoring and control.
CMS.NET provides no special tools to monitor or control content entry.
Restricting Author's Content
An author should be able to access only his own content. In this situation, only one person at a time would be able to update any particular piece of content. Only when the author is finished and has passed the content on to the editors would someone else have write access to the content.
Limiting access in this fashion is easy enough to do. You only need to change the content retrieval method in the AutList Page_Load() method (see Listing 14-3) from the Content database helper GetHeadlines() method to the new GetHeadlinesForAuth() method (see Listing 14-4). This new method selects and retrieves headlines only for the user specified as a parameter, in this case the current user.
Listing 14-3: Restricting Content to the Current Author Only
private void Page_Load(object sender, System.EventArgs e)
{
...
DataTable dt;
int accountNo = account.GetAccountID(User.Identity.Name);
if (accountNo == 1) // Admin sees all content
{
dt = content.GetHeadlines();
}
else
{
dt = content.GetHeadlinesForAuth(accountNo);
}
...
}
Listing 14-4: Content Database Helper GetHeadlinesForAuth Method
public DataTable GetHeadlinesForAuth(int Byline)
{
//SELECT ContentID, Version, Headline, Status
//FROM Content
//WHERE Byline=@Byline
SqlCommand Command =
new SqlCommand("Content_GetHeadlinesForAuth", m_Connection); Command.CommandType = CommandType.StoredProcedure;
Command.Parameters.Add(new SqlParameter("@Byline", SqlDbType.Int));
Command.Parameters["@Byline"].Value = Byline;
SqlDataAdapter DAdpt = new SqlDataAdapter(Command);
DataSet ds = new DataSet();
DAdpt.Fill(ds, "Content");
return ds.Tables["Content"];
}
As a special condition, CMS.NET still wants to be able to get all the content if the user is the administrator, which in the case of CMS.NET is always account 1. In this scenario, CMS.NET continues to use the old GetHeadlines() method to retrieve content because it selects and retrieves all content in the Content database table.
As you can see in Listing 14-4, there really isn't anything special about the Content database helper method GetHeadlinesForAuth(). It is virtually the same as the GetHeadline() method, except it has the additional WHERE clause added to the stored procedure.
Reducing Viewable Content
Over time, the number of articles will become overwhelming because nothing in CMS.NET actually gets deleted from the repository. So, CMS.NET, as shown in Figure 14-4, provides a drop-down box that reduces what an author views on the AutList Web page.
Figure 14-4: The AutList Web page
There are three levels, provided by CMS.NET, of the amount of content listed on the AutList Web page. As you can see in the AutList ASP.NET design code shown in Listing 14-5, the first option of the drop-down list displays only content currently being written. The next option displays active content, content that is being written or edited. The final option displays all content that an author has ever written.
Listing 14-5: The AutList Web Page Drop-Down List
<P>
<STRONG>Which Content: </STRONG>
<asp:dropdownlist id="ddlWhichContent" runat=" server" AutoPostBack=" True" >
<asp:ListItem Value="0">Currently Being Created</asp:ListItem>
<asp:ListItem Value="1">Currently Creating or Editing</asp:ListItem>
<asp:ListItem Value="2">All Written by </asp:ListItem>
</asp:dropdownlist>
</P>
The AutList Codebehind to handle the amount of content viewed can be found, as usual, in the Page_Load() method (see Listing 14-6). As you can see, it is just a simple if statement. The complexities of this if statement are actually hidden within the IsTypeRequested() method, which checks the user-selected value of the drop-down box to see the amount of content the user wants to see. Depending on the level requested, the method then compares each piece of content to see if it falls into the desired level. If so, it is displayed; otherwise, it is simply ignored.
Listing 14-6: User-Determined Reduction of Visible Content in the AutList Method
private void Page_Load(object sender, System.EventArgs e)
{
...
foreach (DataRow dr in dt.Rows)
{
cur = Convert.ToInt32(dr["ContentID"]);
if (cur != prv)
{
prv = cur;
if (IsTypeRequested(dr["Status"].ToString()))
{
...
}
}
}
}
private bool IsTypeRequested(string status)
{
switch(Convert.ToInt32(ddlWhichContent.SelectedItem.Value))
{
case 0:
return StatusCodes.isCreating(status); case 1:
return (StatusCodes.isCreating(status) || StatusCodes.isAwaitingEdit(status) || StatusCodes.isEditing(status));
case 2: return true;
default : return false;
}
}
Other Changes to the AutList Web Page
The AutList Web page also has two additional columns added to it: Status and Notes.
AutList Status Code
Status is a helpful column that enables an author to see where his content is in the workflow. Implement it simply by adding a new heading to the table on the AutList Web page and then, as CMS.NET is looping through the columns to display each headline, place the status in a literal control (see Listing 14-7). The status retrieved from the Content database is of type integer, which must be converted using the ToString() method of the StatusCodes class (see Listing 14-8) to a readable text string.
Listing 14-7: Adding the Status to AutList Web Page
lit = new LiteralControl(StatusCodes.ToString(Convert.ToInt32(dr["Status"])));
cell = new TableCell();
cell.Font.Size = new FontUnit(FontSize.XSmall);
cell.HorizontalAlign = HorizontalAlign.Center;
cell.Controls.Add(lit);
row.Cells.Add(cell);
Listing 14-8: The StatusCodes Common Utility Class
public class StatusCodes
{
public const int None |
= 0x00000000; |
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|||
public const int Creating |
= 0x00000001; |
|
|
|
|
|
|
|
|||
|
|
|
|
|
|
|
|
||||
public const int AwaitingEdit |
= 0x00000002; |
|
|
|
|||||||
|
|
|
|
|
|
|
|
||||
public const int RequiresUpdate |
= 0x00010001; |
||||||||||
|
|
|
|
|
|
|
|||||
public const int Editing |
= 0x00000004; |
|
|
|
|
|
|
||||
|
|
|
|
|
|
|
|||||
public const int AwaitingApproval |
= 0x00000008; |
||||||||||
|
|
|
|
|
|
||||||
public const int RequiresEditing |
= 0x00010004; |
|
|||||||||
|
|
|
|
|
|||||||
public const int Approving |
= 0x00000010; |
|
|
|
|||||||
|
|
|
|
||||||||
public const int Approved |
= 0x00000020; |
|
|
||||||||
|
|
|
|
||||||||
public const int Deployed |
= 0x00000040; |
|
|
||||||||
|
|
|
|||||||||
public const int Archived |
= 0x00000080; |
|
|
||||||||
|
|
||||||||||
public const int Discontinued |
= 0x00100000; |
|
|||||||||
public static string ToString(int val)
{
switch (val)
{
case Creating:
return "Creating"; case AwaitingEdit:
return "Awaiting Edit"; case RequiresUpdate:
return "Requires Update"; case Editing:
return "Editing"; case AwaitingApproval:
return "Awaiting Approval"; case RequiresEditing:
return "Requires Editing"; case Approving:
return "Approving"; case Approved:
return "Approved"; case Deployed:
return "Deployed"; case Archived:
return "Archived"; case Discontinued:
return "Discontinued"; default:
return "None";
}
}
public static bool isCreating(string val)
{
return (Convert.ToInt32(val) & Creating) == Creating;
}
public static bool isAwaitingEdit(string val)
{
return (Convert.ToInt32(val) & AwaitingEdit) == AwaitingEdit;
}
public static bool isRequiresUpdate(string val)
{
return (Convert.ToInt32(val) & RequiresUpdate) == RequiresUpdate;
}
public static bool isEditing(string val)
{
return (Convert.ToInt32(val) & Editing) == Editing;
}
public static bool isAwaitingApproval(string val)
{
return (Convert.ToInt32(val) & AwaitingApproval) == AwaitingApproval;
}
public static bool isRequiresEditing(string val)
{
return (Convert.ToInt32(val) & RequiresEditing) == RequiresEditing;
}
public static bool isApproving(string val)
{
return (Convert.ToInt32(val) & Approving) == Approving;
}
public static bool isApproved(string val)
{
return (Convert.ToInt32(val) & Approved) == Approved;
}
public static bool isDeployed(string val)
{
return (Convert.ToInt32(val) & Deployed) == Deployed;
}
public static bool isArchived(string val)
{
return (Convert.ToInt32(val) & Archived) == Archived;
}
}
AutList Notes
The Notes column enables the author to add comments about his content without changing the content directly. He might use notes to provide information to the editor about what needs to be looked at or to help explain why it was written in a certain way.
To implement this, simply add a call to the notes process. This was discussed earlier.
Submitting Content to Editors
The only other thing of note about the author's process that wasn't covered in Chapter 11 is that AutSubmit (see Listing 14-9) now sets the content status to AwaitingEdit if it is a new content submit or to Editing if the editor is already known. Plus, it builds and sends an EmailAlert to the next role in the workflow, which in this case is the editor.
Listing 14-9: The AutSubmit New Codebehind
private void bnSubmit_Click(object sender, System.EventArgs e)
{
int code;
content.SetStatus(Convert.ToInt32(dt.Rows[0]["ContentID"]),
Convert.ToInt32(dt.Rows[0]["Version"]),
(code = (Convert.ToInt32(dt.Rows[0]["Editor"]) == 0)?
StatusCodes.AwaitingEdit : StatusCodes.Editing));
EmailAlert ea =
new EmailAlert(Context, code, Convert.ToInt32(dt.Rows[0]["Editor"]));
ea.Send();
Response.Redirect("AutList.aspx");
}
The Editing Phase
There are a lot of similarities between the editor's process and the author's process. Obviously, there is no create procedure because that is the job of the author. Three of the other differences you see on the editor's list Web page, EdList.aspx (see Figure 14- 5), are the Edit (instead of the Update) option plus the two new options of Withdraw and Rtn Updt (return to the author for an update).
Figure 14-5: The EdList Web page
Restricting and Reducing Listed Editor Content
Just like the author, the editor can reduce the amount of content that is visible. The code to handle this is virtually the same as that of the author. As you can see in the Codebehind for EdList (see Listing 14-11), reducing the code to only that of the editors is done by the GetHeadlinesForEdit() method (see Listing 14-12).
The editor can reduce the content further by selecting what type of content he wants displayed using the drop-down box generated by the EdList ASP.NET design code (see Listing 14-10).
Listing 14-10: The EdList Web Page Drop-Down Box
<P>
<STRONG>Which Content: </STRONG>
<asp:dropdownlist id=" ddlWhichContent" runat=" server" AutoPostBack=" True">
<asp:ListItem Value="0">Currently Available or Being Edited</asp:ListItem>
<asp:ListItem Value="1">Currently Editing or Awaiting Approval
</asp:ListItem>
<asp:ListItem Value="2">All Edited by</asp:ListItem>
</asp:dropdownlist>
</P>
Listing 14-11: The EdList Codebehind
private void Page_Load(object sender, System.EventArgs e)
{
...
DataTable dt;
int accountNo = account.GetAccountID(User.Identity.Name);
