Simple CMS with Linq to SQL part II

We’ve been putting it aside, but it’s finaly time for editing our content.

Let’s work on editing our content pages first by creating a full page Edit view…

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<SimpleCMS.Models.ContentPage>" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
	Editing - <%: Model.Title %>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
	<h2>Editing - <%: Model.Title %></h2>

	<% Html.RenderPartial("CreatePage", Model); %>

	<div>
		<%: Html.ActionLink("Back to List", "Index") %>
	</div>
</asp:Content>

Note, if we’re going to use the same CreatePage control as before to edit our pages as well as create them, then we have to include some functionality within the control to know which mode to show when.

Open up the CreatePage control and modify the top and bottom portions of the control. The middle will stay essentially the same.

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<SimpleCMS.Models.ContentPage>" %>
<div class="postform">
	<%
		// If this model has a title, then we're editing an existing page
		var mode = (string.IsNullOrEmpty(ViewData.Model.Title)) ? "Create" : "Edit";
		
		using (Html.BeginForm(mode, "Pages", new { id = ViewData.Model.PageId }, FormMethod.Post))
		{%>

	<%: Html.ValidationSummary(true)%>

	<fieldset>
		<legend>Create new page</legend>
		<p>
			<label for="Title">Title</label>
			<%: Html.TextBoxFor(model => model.Title)%><%: Html.ValidationMessageFor(model => model.Title, "*")%>
		</p>
		<p>
			<label for="Description">Description</label>
			<%: Html.TextBoxFor(model => model.Description)%>
		</p>
		<p>
			<label for="Abstract">Abstract</label>
			<%: Html.TextAreaFor(model => model.Abstract, new { rows = 5, cols = 60 })%>
		</p>
		<p>
			<label for="Author">Author</label>
			<%: Html.TextBoxFor(model => model.Author)%>
		</p>
		<p>
			<label for="BodyText">Content</label>
			<%: Html.TextAreaFor(model => model.BodyText, new { rows = 5, cols = 60 })%>
			<%: Html.ValidationMessageFor(model => model.BodyText)%>
		</p>
		<p>
			<label for="PubDate">Publish Date</label>
			<%: Html.TextBoxFor(model => model.PubDate)%>
		</p>
		<ul class="feedback options">
			<li><label><input type="checkbox" value="true" name="EnableComments" 
			<%= Html.CheckBoxChecked(ViewData.Model.EnableComments, true) %>/> Enable Comments</label></li>
			<li><label><input type="checkbox" value="true" name="AnonComments" 
			<%= Html.CheckBoxChecked(ViewData.Model.AnonComments, true) %>/> Anonymous Comments</label></li>
			<li><label><input type="checkbox" value="true" name="ModeratedComments" 
			<%= Html.CheckBoxChecked(ViewData.Model.Moderated, false) %>/> Moderated</label></li>
			<li><label><input type="checkbox" value="true" name="Approved" 
			<%= Html.CheckBoxChecked(ViewData.Model.Approved, true) %>/> Approved</label></li>
		</ul>

		<p>
		<% if(mode == "Create")  { %>
			<input type="submit" value="Create new page" />
		<% } else { %>
			<input type="submit" value="Save changes" />
		<% } %>
		</p>
	</fieldset>
</div>
<% } %>

Note how we’re checking for a page title. If you remember from the last installment, the page Title was a required field in our database. Not having a title means this model was never in the database, hence brand new.

And we similarly use the mode flag to change what our submit button will say on the bottom of the page. It’s very simple, but it works.

Of course now that we have the views for editing a page, we also need to have the controller setup for it.

Let’s edit the Edit ActionResults in our controller…

public ActionResult Edit(int? id)
{
	// Page Id
	id = id ?? 0;

	if (id.Value > 0)
	{
		using (SimpleCMSDataContext db = new SimpleCMSDataContext())
		{
			ContentPage content = (from p in db.ContentPages
						   where p.PageId == id.Value
						   select p).Single();


			ViewData.Model = content;
			return View();
		}
	}
	return RedirectToAction("Index");
}



[HttpPost]
public ActionResult Edit(int? id, FormCollection collection)
{
	// Page Id
	id = id ?? 0;

	if (id.Value > 0)
	{
		using (SimpleCMSDataContext db = new SimpleCMSDataContext())
		{
			ContentPage content = (from p in db.ContentPages
						   where p.PageId == id.Value
						   select p).Single();

			ContentPage edited = GetPageFromCollection(id.Value, collection, false);

			content.Title = edited.Title;
			content.Description = edited.Description;
			content.Abstract = edited.Abstract;
			content.BodyHtml = edited.BodyHtml;
			content.BodyText = edited.BodyText;

			content.Approved = edited.Approved;
			content.AnonComments = edited.AnonComments;
			content.Moderated = edited.Moderated;

			db.SubmitChanges();
		}

		return RedirectToAction("Read", new { id = id.Value });
	}
	return RedirectToAction("Index");
}

Note how we’ve added an extra parameter “false” to our GetPageFromCollection helper in the controller. This is because there are certain parameters that shouldn’t be altered when making edits.

This also means that we need to change our previous Create ActionResult and add “true” as the paramter. And here’s why…

private ContentPage GetPageFromCollection(int id, FormCollection collection, bool newpage)
{
	// Setup markdown
	Markdown m = new Markdown();

	// New page
	ContentPage content = new ContentPage();

	// Trigger date
	DateTime dt = DateTime.Now;

	// Basics
	content.ParentId = id;
	content.Title = Util.DefaultString(collection["title"], "No title");
	content.Description = Util.DefaultString(collection["description"], "");

	if (newpage)
	{
		content.Author = Util.DefaultString(collection["author"], "Anonymous"); // Just for now
		content.AuthorId = 0; // Just for now
	}

	// Page content
	content.Abstract = Util.DefaultString(collection["abstract"], "");
	content.BodyText = Util.DefaultString(collection["bodytext"], "");
	content.BodyHtml = m.Transform(content.BodyText);


	// Eenable publishing
	content.Approved = Util.DefaultBool(collection["approved"], true);

	if (newpage)
	{
		// Nested
		content.ViewCount = 0;
		content.CommentCount = 0;
		content.PageCount = 0;

		// Times
		content.CreatedDate = dt;
	}

	// Pubdate
	content.PubDate = Util.DefaultDate(collection["pubdate"], dt);

	// Every time this function is called, we're doing something to the page.
	content.LastModified = dt;

	// Feedback
	content.EnableComments = Util.DefaultBool(collection["enablecomments"], true);
	content.AnonComments = Util.DefaultBool(collection["anoncomments"], true);
	content.Moderated = Util.DefaultBool(collection["moderated"], false);

	return content;
}

This means that we also have to change the GetCommentFromCollection helper to match as well, because editing comments is next.

Just remember to add the “true” flag to the function when we call it in the CreateComment ActionResult.

private ContentComment GetCommentFromCollection(int id, FormCollection collection, bool newcomment)
{
	// Setup markdown
	Markdown m = new Markdown();

	// New comment
	ContentComment comment = new ContentComment();

	// Trigger date
	DateTime dt = DateTime.Now;

	// Author name
	comment.Author = Util.DefaultString(collection["author"], "Anonymous");
	
	if (newcomment)
	{
		// Basics
		comment.PageId = id;
		comment.AuthorId = 0; // Just for now
		comment.AuthorIP = Util.GetUserIP();
		comment.CreatedDate = dt;
	}
			
	comment.LastModified = dt;

	// We don't have an approval yet
	comment.Approved = true;
	comment.AuthorEmail = Util.DefaultString(collection["authoremail"], "");

	// Content
	comment.BodyText = Util.DefaultString(collection["bodytext"], "");
	comment.BodyHtml = m.Transform(comment.BodyText);

	return comment;
}

At this moment, I should point out that I overlooked something in our Util.DefaultString function. In that utilites class, I neglected to take into consideration that we’ll be storing the multiline content in SQL server in an nvarchar field.

When this is the case, we actually need to make sure that the newline character is preserved. The only way to do this is to modify the DefaultString method of our Util class in the Helpers folder.

public static string DefaultString(string val, string d)
{
	if (string.IsNullOrEmpty(val)) val = d;

	val.Replace("\r", Environment.NewLine);
	val.Replace("\n", Environment.NewLine);

	return val;
}

Now we can create, read and edit content pages. Let’s move on to making edits to content comments.

Onward to step 4…

3 thoughts on “Simple CMS with Linq to SQL part II

  1. Pingback: Simple CMS with Linq to SQL part II-a « This page intentionally left ugly

  2. I don’t know what this is all about, but it could sure use a more tag right near the top of the first page! (you don’t have to publish this)

    • thinsmek, I don’t censor comments that aren’t outright spam or have hate speech ;)

      I used the next best thing to the more tag… the nextpage tag.

      The more tag had caused some issues with my blog in the past, which is why I rarely use it. A lot of my content is raw code, so that may have something to do with it. This is an introduction to writing a content management system from scratch.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s