ASP.NET Social Networks—Blogs in Fisharoo

Problem

This article, as stated in Introduction, is all about adding the Blogging feature to our site. This will handle creating and managing a post. It will also handle sending alerts to your friends' filter page. And finally we will handle creating a friendly URL for your blog posts. Here we are making our first post to our blog:

Once our post is created, we will then see it on the Blogs homepage and the My Posts section. From here we can edit the post or delete it. Also, we can click into the post to view what we have seen so far.

The following screenshot shows what one will see when he/she clicks on the post:

I have the blog post set up to show the poster's avatar. This is a feature that you can easily add to or remove. Most of your users want to be able to see who the author is that they are currently reading!

Also, we will add a friendly URL to our blog post's pages.

Design

The design of this application is actually quite simple. We will only need one table to hold our blog posts. After that we need to hook our blog system into our existing infrastructure.

Blogs

In order for us to store our blog, we will need one simple table. This table will handle all the standard attributes of a normal blog post to include the title, subject, page name, and the post itself. It has only one relationship out to the Accounts table so that we know who owns the post down the road. That's it!

Solution

Let's take a look at the solution for these set of features.

Implementing the database

Let's take a look at the tables required by our solution.

Blogs

The blogs table is super simple. We discussed most of this under the Blogs section.

The one thing that is interesting here is the Post column. Notice that I have this set to a varchar(MAX) field. This may be too big for your community, so feel free to change it down the road. For my community I am not overly worried. I can always add a UI restriction down the road without impacting my database design using a validation control. After that we will look at the IsPublished flag. This flag tells the system whether or not to show the post in the public domain. Next to that we will also be interested in the PageName column. This column is what we will display in the browser's address bar. As it will be displayed in the address bar, we need to make sure that the input is clean so that we don't have parsing issues (responsible for causing data type exceptions) down the road. We will handle that on the input side in our presenter later.

Creating the relationships

Once all the tables are created, we can then create all the relationships. For this set of tables we have relationships between the following tables:

Blogs and Accounts

Setting up the data access layer

To set up the data access layer follow the steps mentioned next:

Open the Fisharoo.dbml file.

Open up your Server Explorer window.

Expand your Fisharoo connection.

Expand your tables. If you don't see your new tables try hitting the Refresh icon or right-clicking on tables and clicking Refresh.

Then drag your new tables onto the design surface.

Hit Save and you should now have the following domain objects to work with!

Keep in mind that we are not letting LINQ track our relationships, so go ahead and delete them from the design surface. Your design surface should have all the same items as you see in the screenshot (though perhaps in a different arrangement!).

Building repositories

With the addition of new tables will come the addition of new repositories so that we can get at the data stored in those tables. We will be creating the following repository to support our needs.

BlogRepository

Our repository will generally have a method for select by ID, select all by parent ID, save, and delete. We will start with a method that will allow us to get at a blog by its page name that we can capture from the browser's address bar.

Notice that for this system to work we can only have one blog with one unique page name. If we forced our entire community to use unique page names across the community, we would eventually have some upset users. We want to make sure to enforce unique page names across users only for this purpose. To do this, we require that an AccountID be passed in with the page name, which gives our users more flexibility with their page name overlaps! I will show you how we get the AccountID later. Other than that we are performing a simple lambda expression to select the appropriate blog out of the collection of blogs in the data context.

Next, we will discuss a method to get all the latest blog posts via the GetLatestBlogs() method. This method will also get and attach the appropriate Account for each blog. Before we dive into this method, we will need to extend the Blog class to have an Account property.

To extend the Blog class we will need to create a public partial class in the Domain folder.

The first expression in this method gets the top N blogs ordered by their UpdateDate in descending order. This gets us the newest entries. We then add a where clause looking for only blogs that are published.

We then move to getting a list of Accounts that are associated with our previously selected blogs. We do this by selecting a list of AccountIDs from our blog list and then doing a Contains search against our Accounts table. This gives us a list of accounts that belong to all the blogs that we have in hand.

With these two collections in hand we can iterate through our list of blogs and attach the appropriate Account to each blog. This gives us a full listing of blogs with accounts.

As we discussed earlier, it is very important for us to make sure that we keep the page names unique on a per user basis. To do this we need to have a method that allows our UI to determine if a page name is unique or not. To do this we will have the CheckPageNameIsUnique() method.

This method looks at all the blog entries except itself to determine if there are other blog posts with the same page name that are also by the same Account. This allows us to effectively lock down our users from creating duplicate page names. This will be important down the road when we start to discuss our pretty URLs.

Next, we will look at a private method that will help us clean up these page name inputs. Keep in mind that these page names will be displayed in the browser's address bar and therefore need not have any characters in them that the browser would want to encode. While we can decode the URL easily, this conversation is more about keeping the URL pretty so that the user and search engine spiders can easily read where they are at. When we have characters in the URL that are encoded, we will end up with something like %20 where %20 is the equivalent to a space. But to read my%20blog%20post is not that easy. It is much easier to ready my-blog-post. So we will strip out all of our so called special characters and replace all spaces with hyphens. This method will be the CleanPageName() method.

You can add to this as many filters as you like. For the time being I am replacing the handful of special characters that we have just seen in the code. Next, we will get into the service layers that we will use to handle our interactions with the system.

Implementing the services/application layer

Once all the repositories are built for single serving purposes, we can begin to create the services layer. Again, this layer is responsible for assembling aggregates and performing complex actions with our entities. We will not be creating any new services for this component but will need to add to the following existing service:

AlertService

AlertService

The AlertService is responsible for sending out notifications to our users via their filter page. This is the page that shows new activity amongst your profile and your friends' profiles.

Now we will quickly take a look at the AddNewBlogPostAlert() method. This method will take in the new Blog that was posted so that we can use some information about it in our alert. As soon as we get into the body of our method, we want to initialize a new Alert. We will then fill out some of the initial properties.

One of the properties that we will need to extend is the AlertType class that has the AlertTypes property. You will see that this AlertTypes property is really a representation of the record IDs we have stored in the AlertTypes table in the database. Open up the AlertType class and add a couple of new entries for the NewBlogPost and UpdatedBlogPost.

Then go into the AlertTypes table and create two new records-NewBlogPost and UpdatedBlogPost. If the record IDs that are generated do not correspond to the numbers you see above,update the numbers you see in the enum to the ones that were created in the table.

Now we can look at the message that we want to show in our alert. This can consist of any standard HTML as it will be displayed on the alerts page or "the filter" as we will call it. Once the Alert is fully configured, we can then save the Alert to the database. And in this case (but not all cases) we want this alert to show up on all of our friends' filters as well, to let them know that a new blog post was just created.

Here is the method for the updated blog post, which is almost identical to the one seen for updating the blog post:

With the repository and service layers completed, we can now take a look at our UI.

Implementing the presentation layer

The presentation layer is almost as simple as the infrastructure is. We will have a Blog link in the top navigation. When you click on this link you will be taken to a page where you see a list of the latest blog posts. From the Blog section you can then choose to either view a blog from the latest blogs page or you can choose to view your blogs or create a new blog. In addition to these four pages we will also address how fancy URLs come into play in the view post page.

Latest blog posts

Viewing the latest blog posts is a single call to the BlogRepository. In order for our UI to get to any repository though, it first has to hand off its control of all display interactions. It does this by initializing an instance of the DefaultPresenter and then passing a reference to itself into the Init() method of the presenter. Once in the Init() method of the presenter we can then make the call into the repository to get the latest blogs.

Also note that we have the ItemDataBound() method to handle each item that is bound. This will allow us to configure all the controls in the UI for each set of objects. Notice in particular that at the very end of our ItemDataBound() method, that we are configuring the NavigateUrl property to display fancy URLs! This will be important to remember when we start our discussion about the ViewPost.aspx page.

Out in the UI side we can see how all the data sections are bound to the container's blog items.

<EmptyDataTemplate> Sorry, there are no blogs posted yet! </EmptyDataTemplate></asp:ListView>

If we had data in the system we would now see a list of the latest blogs!

My blog posts

The My Blog Posts section is 99.999% identical to the latest blogs post with the exception that they load their list of Blog objects via a different call into the same BlogRepository. In this case we get a list of Blogs by calling the GetBlogsByAccountID() method. This method will get a list of our blog posts sorted by their create date.

Fancy URL support

Now that we have both the recent blog posts and the my posts pages created and out of the way, we need to discuss handling the pretty URLs that we have our UIs currently displaying. At the moment we are sending people to domain.com/blogs/username/pagename. As you may have guessed, this is a path to a resource that does not actually exist. In order to handle the unknown resources, we will have to extend the UrlRewrite class that we have in the Handler's directory.

In the UrlRewrite class just seen, notice that I added a reference to the BlogRepository so that we can get the blog in question if that is indeed what this rewrite is for. Next, notice that I removed the .aspx extension from the list of extensions to exclude it from processing. This is because we want our pages to look like real pages even though they are actually dynamic (read non-existent) resources.

After that we test to see if we are working with a blog redirection. If we are, then we extract the user's username and the page name from the URL. With this information in hand we can locate the Blog that we need. From there we can easily do a redirection to the page as though the user had no idea. To them the pretty URL stays intact just as it was when they entered it or followed it.

Now when rewriting the URL on the server side you have to be aware that the local path "~" identifier may no longer work as expected. In my case it makes all images load as though the blog directory is the root directory. So for this reason you will notice that the ViewPost.aspx page has items in its UI with root level mappings in the standard HTML fashion /images/resource rather than ~/images/resource. This fixes the issue without any problem. Everything else should work as expected.

View post

The ViewPost.aspx page is an amazingly straightforward page to build. It is extracting the page to be viewed from the URL by way of the rewritten URL, which contains the BlogID behind the scenes. The ViewPostPresenter gets to the BlogID through the WebContext.BlogID property.

The thing to be pay attention to, as I mentioned before, is that all the paths are in a fixed format off the root of the site. This way no matter where we are at, we know where to go to gain access to the specified resource.

Create or edit post

With all of this work out of our way we can now turn our attention to the dirty work of creating the actual blog post. This page will actually serve two purposes. We need to use it to create our blog post. But we will also repurpose the UI to edit already existing posts as well. Let's look at the presenter for this page:

In the first section of the Init method we are checking the WebContext.BlogID property to see if we have something to work with. If we do then we load the UI with the appropriate blog. The next item you see is the SavePost method that takes care of passing a loaded blog into the BlogRepository to be saved.

Next, we will take a look at the code behind that the presenter works with. Here we will see the LoadPost and btnSave_Click methods. There is nothing fancy to follow here. But the one aspect to pay attention to is that we keep track of the BlogID in the page so that we know what we are working with later.

This takes care of the little details of data inputs and outputs. However, let's now take a look at what we need to do make the UI somewhat useable. We will make use of Xinha WYSIWYG editor. All that is required to hook this up is a multiline text box control and a single line of JavaScript.

Summary

In this article we covered the creation of blog posts. We also went over the pages that are needed for people to see other users' posts as well as their own. And of course we provided a page to actually read a post. In addition to this we covered the concept of fancy or pretty URLs that are more user as well as search engine friendly. Finally, we added a touch more usability to our UI in the form of the Xinha WYSIWYG.

Alerts & Offers

Series & Level

We understand your time is important. Uniquely amongst the major publishers, we seek to develop and publish the broadest range of learning and information products on each technology. Every Packt product delivers a specific learning pathway, broadly defined by the Series type. This structured approach enables you to select the pathway which best suits your knowledge level, learning style and task objectives.

Learning

As a new user, these step-by-step tutorial guides will give you all the practical skills necessary to become competent and efficient.

Beginner's Guide

Friendly, informal tutorials that provide a practical introduction using examples, activities, and challenges.

Essentials

Fast paced, concentrated introductions showing the quickest way to put the tool to work in the real world.

Cookbook

A collection of practical self-contained recipes that all users of the technology will find useful for building more powerful and reliable systems.

Blueprints

Guides you through the most common types of project you'll encounter, giving you end-to-end guidance on how to build your specific solution quickly and reliably.

Mastering

Take your skills to the next level with advanced tutorials that will give you confidence to master the tool's most powerful features.

Starting

Accessible to readers adopting the topic, these titles get you into the tool or technology so that you can become an effective user.

Progressing

Building on core skills you already have, these titles share solutions and expertise so you become a highly productive power user.