Berlin, Germany

Please login...

This website is restricted to invited-only users. Please login with your credentials.

Having trouble?

Having trouble with this site? Then please feel free to contact Steven Kohus.

Blog Entry #12

jQuery Custom Validation Function

by Steven Tuesday, May 31, 2011 @ 1:51:00 PM

I like the jQuery Validator plugin (http://bassistance.de/jquery-plugins/jquery-plugin-validation/) - it is robust, easy to configure, and highly extensible.  Here is a basic example of configuring it to validate some address controls:

$(document).ready(function() {
		// Set up the validation rules for this page.
		var validator = $("#frmRegister").validate({
			rules: {
				"addAddress1": {
					required: true,
					minlength: 3
				},
				"addCityName": {
					required: true
				},
				"addZip": {
					required: true,
					minlength: 5,
					number: true
				}
			},
			messages: {
				"addAddress1": {
					required: "Please enter an address.",
					minlength: jQuery.format("The address is too short.")
				},
				"addCityName": {
					required: "Please enter a city name."
				},
				"addZip": {
					required: "Please enter a zip.",
					minlength: jQuery.format("Enter at least {0} characters."),
					number: "Please enter a valid zip."
				}
			},
			// the errorPlacement has to take the table layout into account
			errorPlacement: function(error, element) {
				if ( element.is(":radio") )
					error.appendTo( element.parent().next().next() );
				else if ( element.is(":checkbox") )
					error.appendTo ( element.next() );
				else
					error.appendTo( element.parent().next() );
			},
			// specifying a submitHandler prevents the default submit, good for the demo
			submitHandler: function(form) {
				form.submit();
			},
			// set this class to error-labels to indicate valid fields
			success: function(label) {
				// set   as text for IE
				label.html(" ").addClass("checked");
			}
		});
	});

First you create rules, which specify the control to validate along with what rules apply to it.  For example, "addZip" refers to a control with that name, and says it is a required field (can not be blank), it must have a minimum of 5 characters, and that it must be numeric.  Fairly simple, you can read the documentation (http://docs.jquery.com/Plugins/Validation) for more rules that can be applied.  Second comes the messages to be displayed if one of the validators is triggered.  The third parameter specifies where the error messages should be displayed.  You can display them any way you choose, but the above sample assumes a table layout and puts the error message in the column after the field.  The fourth parameter is a submit handler, which is where you can submit the form or do any type of pre-processing before the form is submitted.  The last function is for displaying a successful CSS class if the fields validate properly.

Some things to keep in mind:

  • Quotations marks are required if the name attribute contains characters that will conflict with jQuery.  This is not a big issue with .NET, however in Struts2 with Java you will often have fields named like "personProfile.address.addZip" - the name must be surrounded with quotes since periods are used differently with jQuery.
  • Messages can be formatted strings to display variables used within the rule settings. 
  • The form will not submit (in this case) until all fields are valid.  You can add a class "cancel" to have it not validate specific fields. 
  • Be careful not to use a jQuery selector for the form submit action ($(form).submit();) - this will cause a recursive call to the validation endlessly.  Use the native form.submit();
  • There are multiple ways to apply rules to elements, the above I believe is generally cleaner and easier to understand, but you can also apply specific classes to elements and they will validate using the default behavior as will be demonstrated below.

As I mentioned previously, it is highly extensible as well.  Besides all of the built-in types of validations, you can also write your own custom validation function and add it to one of the rules.  Simply use the jQuery.validator.addMethod() function to create your own rule:

var today = new Date();
var year = today.getFullYear();
		
jQuery.validator.addMethod("validyear", function( value, element ) {
	var result = true;
	if (this.optional(element) || value.length == 0)
		result = true;
	else if (value.length > 0 && value.length < 4)
		result = false;
	else
	{
		var current = +value;
		if (current < 1800 || current > year)
			result = false;
	}
	return result;
}, "Please enter a valid year (1800 - " + year + ").");

The above code takes an input element, and checks to see if the value entered is a valid year in the range of 1800 to the current year.  To apply this custom validation function, you can add it as one of the rules above, or you can apply it directly to a field by simply adding a class of the same name as what you called the custom function (in this case "validyear").  I hope this gives a small glimpse into this very useful plugin.

 Here are some nice demos of the validator plugin for those interested:

Blog Entry #11

Setting Up Wifi on XMBC Zotac Box

by Steven Sunday, February 13, 2011 @ 4:38:00 PM

I recently wanted to create a home theatre system for my girlfriend Jasmine, because she loves movies a lot. I wanted something that would be easy to use and impressive interface to make the process of browsing for movies enjoyable.  I looked at several options for this, but I decided to go with XBMC for numerous reasons.  It was free (always a good thing), it can run on Linux/Windows/Mac computers, it was lightweight, and with a dedicated development community, it had tons of additional features that could be added to it.

I purchased a Zotac N330 box that came without any OS which was on sale for less than $200.  It is a tiny box, but it seemed to suit my needs perfectly (2 GB of RAM, wireless card, small form factor, 160 GB internal drive).  Installing XBMC Live on it (with Ubuntu 10.04 as the underlying OS) went pretty smoothly for the most part (a small hiccup with getting it to install via a thumbdrive).  However once I got everything up and running, I ran into one huge problem: internet connection wasn't working. 

Trying to search for XBMC solutions didn't help much as it is based on the underlying OS.  So I had to spend hours searching forums and finding solutions that worked for others but didn't help me at all.  Even if it seemed like the exact same problem as I had.  I wanted to keep my network as secure as possible, but it seemed quite difficult to set up WPA2 access via the command line.  I called my friend Louis who is a Linux guru, and he mentioned a lot of issues he had with it as well.  He pointed me in the right direction of using wpa_supplicant, which luckily was already a part of the Live distro I was using.

Hopefully these steps may help someone else having a similar Wifi connection issues with XBMC.

Open the interfaces file, which for me was located in /etc/network/interfaces:

sudo nano /etc/network/interfaces

And then add the following configuration options to the file:

# The loop back interface, leave this alone
auto lo 
iface lo inet loopback

# The wireless interface, yours might be named differently than wlan0
auto wlan0
iface wlan0 inet dhcp
wireless-essid your_ssid
pre-up wpa_supplicant -B -Dwext -iwlan0 -c/etc/wpa_supplicant.conf
pre-down killall -q wpa_supplicant

DHCP will give your box a dynamic IP address from the router.  You can also use a static IP if you prefer.  Replace "your_ssid" with the name of your wireless network that you want to connect to.  "wext" is the wireless driver to use, it is generic but should work for most cases.  The config file we will create shortly.  If you don't know the name of your wireless card, you can run:

iwlist scan

This will show all the network devices, but most likely yours is called wlan0 as well.

Before we can create the config file, we need to convert the wireless network password from ASCII to the format that wpa_supplicant expects.  There is a usefull utility for this:

wpa_passphrase your_ssid your_ascii_password

# To make the next step easier, you might as well pipe the output to the conf file:

sudo wpa_passphrase your_ssid your_ascii_password > /etc/wpa_supplicant.conf

Now to create the config file that will store our network settings:

sudo nano /etc/wpa_supplicant.conf

And copy this code into it:

ctrl_interface=/var/run/wpa_supplicant

network={
ssid="your_ssid"
scan_ssid=1
proto=WPA RSN
pairwise=CCMP TKIP
group=CCMP TKIP
psk=your_generated_password
}

scan_ssid = 1 to broadcast the ssid, 2 to stay silent
proto = The protocols used, WPA = WPA1, RSN = WPA2
pairwise = The encryption protocols used, CCMP = AES, TKIP = TKIP
group = Same as above
psk = The password generated from wpa_passphrase utility (this is important, ASCII password will not be recognized)

With all that set, we should be able to restart the networking service:

sudo /etc/init.d/networking restart

 

I hope that helps someone out there as I spent a couple days trying many solutions that didn't work for me.

Blog Entry #10

Project Jarvis.NET

by Steven Tuesday, January 11, 2011 @ 5:14:00 PM

Recently on Cracked.com they featured a list of DIY (do-it-yourself) projects that were pretty amazing.  Examples of people creating helicopters and submarines, and even taking pictures of the curvature of the earth with a weather balloon kit costing less than $200.  But it was the #1 pick that got me interested as a software engineer, a movie-inspired AI assistant called Jarvis, from the movie Iron Man (Project Jarvis). 

It was written to perform many tasks, including turning on/off the lights, telling you the weather where you currently were based on your GPS coordinates, showing you your Netflix que, showing your tracking package status, displaying social networking status updates, and much more.  You can see more about that project here.  It wasn't truly an AI program, but it was cool nonetheless.  What I liked most about it is that it integrated many aspects of his life together and gave him more control of his surroundings.  What I disliked most about the project is that it wasn't free/open source (edit: it might be open source someday), and it was written in AppleScript - which I don't even own a Mac.  There are many similar projects to this, like Project Oris - which is written in .NET, but suffers from the same problem of not being free/open source. 

Therefore I was seriously considering to write my own, both for fun and as a learning experience.  At work I am hoping to become a technical architect, so I think this would be a good experience for that as well.  In my head I have already started to plan the system architecture of the software.  I wanted to write a system core that would handle the universal functionalities needed by the system, such as a text-to-speech and speech-recognition engine, communicating commands, UI features, shell script access, running in the background, auto-start with Windows, and much more.  Then allow the system to be extended via plugins and have the plugins be grouped by categories.  For instance there could be a social networking category and users can download/enable plugins for social networks that they wish to receive updates for or autopost to.  Another example would be a home category, where users can turn on/off their lights, start playing music or a movie without going to the PC, start torrenting files remotely, start large batch processes such as printing albums remotely, controlling the temperature dynamically, monitor electricity useage, and starting/stopping a webcam that can be viewed online.

It can quickly grow to a huge project, so I am hoping the plugin system can make it expandable and customizeable so that even if I only finish a portion of it, it will still be fully functional without the wait for every feature to be implemented.  I am also not sure if I will get bored of this project or not have enough time for it (several looming deadlines at work coming up), but I figure it will let me play with some fun technologies that I don't have much experience with, which I can blog about as I go, such as speech engines, plugin architecture, remote communications via IM or web API's, RFID controls - if I ever purchase the hardware for it, and much more.

Anyways, that is my idea.  Stay tuned for further updates.

Blog Entry #9

Sorting Blog Posts by Page Views

by Steven Sunday, January 2, 2011 @ 9:46:00 PM

As I customize the BlogEngine.NET blog further and get more in-depth into the code, I realize there are some missing features that I felt would be included in any engine.  One particular missing feature is the ability to sort and display blog posts by the number of page views ("Popular" posts so-to-speak).  There is support for displaying blog posts by date, author, and category.  There is also underlying code (with minimum tweaks) to support sorting by number of comments, rating, and number of raters among other things.  However there is no tracking of blog post views in the system, therefore it required a different approach.

You will first need some way of tracking your page views, and I am not going to cover that in this blog.  There are dozens of counter scripts out there, and quite easy to write your own as well.  An example would be a simple XML file that stored the page name or blog GUID along with the number of views for that page.  Then on your MasterPage Page_Load(), add some code to see if the current page is in the file, if so then increment the views by 1, else add it to the file.

The next step is creating a correlation between the blog post object and the number of views.  You could simply add a View property to the Post class and recompile the BlogEngine.Core.dll, however this will make it harder to upgrade to newer versions of BlogEngine.NET in the future.  So I decided to create a wrapper class instead:

/// <summary>
/// Wrapper class for BlogEngine.NET Post class, that includes number of views too that can be sorted based on view count.
/// </summary>
public class PostViews : IComparable
{
    // Properties to store the blog post data along with it's number of views.
    public Post Post { get; set; }
    public int Views { get; set; }

    // Generic constructor.
    public PostViews(Post post, int views)
    {
        this.Post = post;
        this.Views = views;
    }

    /// <summary>
    /// Used for sorting the posts by view count.
    /// </summary>
    /// <param name="obj">The PostViews object to compare the view count with.</param>
    /// <returns>Returns an integer signifying the sort order (1 = higher, 0 = equal, -1 = lower).</returns>
    public int CompareTo(object obj)
    {
        return this.Views.CompareTo(((PostViews)obj).Views);
    }
}

This is a really basic wrapper class.  There are two properties, one is the Post object (you will need to add using BlogEngine.Core; to your using's list), the other is the number of page views for that blog post.  The only other addition is the implementation of the IComparable interface, which will make sorting the list much easier later on.  It implements the CompareTo() function, and tells any collection class that supports IComparable how to sort two objects of this class (in this case a simple integer comparison).

It then becomes simple to sort them based on page views:

List<PostViews> popular = new List<PostViews>();    // The list of posts that will be sorted on view count.
List<Post> posts = Post.Posts;                      // The original list of Posts;

// Copy the posts to the array of the wrapper class.
foreach (Post p in posts)
{
    popular.Add(new PostViews(p, GetPageViews(p.Slug)));
}
popular.Sort();     // Uses CompareTo function to automatically sort based on page views.

You will have to write your own function GetPageViews(), to pull from your data source the number of page views for each post.  I would suggest using the Post GUID as a parameter instead since it will be an unique value, however in my case I use the page name to keep track of page views for non-blog pages as well, so the slug worked better for me - I have to be careful not to have two blogs without the same name though.

That is all there is to it.  Either print out the information manually or bind it to the PostList.ascx custom control. 

Some ideas/suggestions:

  • If you want to sort on multiple properties in your wrapper class, consider using the IComparer interface.  This way you can have multiple sorting functions, and call the one you need.
  • This code will sort them in ascending order (lowest-to-highest).  If you want descending order (highest-to-lowest), you can either multiply the return value in the CompareTo function by -1 or add popular.Reverse(); after the Sort() function call.
  • If you don't plan to upgrade your BlogEngine.NET instance, or if you are already making changes to the core DLL (and hopefully keeping track of those changes!), then it is probably much better to integrate the page views code inside of the DLL as well.
  • Once the popular list is sorted, consider putting it into the Cache, so you don't have to keep performing this operation every time you get a request to view the popular posts (this is mainly for high-traffic sites or those with a very large amount of blogs).
  • This is just my solution, there may be better ways to do this, but it is how I solved the issue.

 

Blog Entry #8

Thailand 2007

by Steven Tuesday, December 28, 2010 @ 12:34:00 AM

Here are some of my favorite pictures from my trip to Thailand in 2007.  It is a very beautiful country with lots to see and do.  I traveled to many different cities, from Chiang Mai in the north, to Bangkok and Chon Buri in the south.  I preferred the north since a lot more cultural activities and less tourists, but some very beautiful beaches in the south.  Bangkok is a very modern city and feels a lot like an American city - probably the easiest place to visit for foreign tourists that want a taste of Thailand without going too far out of their comfort zone.  However, those that want to see more of cultural Thailand, I would highly recommend Chiang Mai.

/home/pics/thailand2007/923447237_f9db4fdc4a.jpg   /home/pics/thailand2007/923447467_2866d5c7e2.jpg   /home/pics/thailand2007/923448339_a79a797dc0.jpg   /home/pics/thailand2007/923448571_62bd455cfb.jpg   /home/pics/thailand2007/923450411_cb8133293d.jpg   /home/pics/thailand2007/923614347_76b9a473ef.jpg   /home/pics/thailand2007/923614785_4f74534766.jpg   /home/pics/thailand2007/923615097_a5ca473a5e.jpg   /home/pics/thailand2007/923616321_2d36ccbc02.jpg  
/home/pics/thailand2007/923616547_9aaf5c4743.jpg   /home/pics/thailand2007/923616961_a408a3decc.jpg   /home/pics/thailand2007/923617669_9d3433469c.jpg   /home/pics/thailand2007/923618565_352292f5f6.jpg   /home/pics/thailand2007/923618955_ca0064b4c0.jpg   /home/pics/thailand2007/923619195_e913dab1a1.jpg   /home/pics/thailand2007/923619813_256428f2d1.jpg   /home/pics/thailand2007/923620567_51d42d439f.jpg   /home/pics/thailand2007/923621283_d70699daf1.jpg  
/home/pics/thailand2007/924297960_2e3b470f18.jpg   /home/pics/thailand2007/924298158_47ed25142b.jpg   /home/pics/thailand2007/924298428_b7735c2bfd.jpg   /home/pics/thailand2007/924300562_0cd9da3309.jpg   /home/pics/thailand2007/924300916_27a3d7f7a0.jpg   /home/pics/thailand2007/924464344_a7d2cf1bbf.jpg   /home/pics/thailand2007/924465156_3951505595.jpg   /home/pics/thailand2007/924465602_1059d08efe.jpg   /home/pics/thailand2007/924467480_c1878228e9.jpg  
/home/pics/thailand2007/924468360_f872e36eff.jpg   /home/pics/thailand2007/924468960_821a41d3fb.jpg   /home/pics/thailand2007/924471820_96a27ab483.jpg   /home/pics/thailand2007/929632971_f32fdd346b.jpg   /home/pics/thailand2007/929635087_2914a3515a.jpg   /home/pics/thailand2007/929636623_afb54778f0.jpg   /home/pics/thailand2007/929637975_c72113a313.jpg   /home/pics/thailand2007/929638319_dc8e96aacc.jpg   /home/pics/thailand2007/929639835_b65df40d02.jpg  
/home/pics/thailand2007/929641655_e3a9044719.jpg   /home/pics/thailand2007/929862725_5b9a093cca.jpg   /home/pics/thailand2007/929873013_da16ff43ab.jpg   /home/pics/thailand2007/929874449_1d4ffc1077.jpg   /home/pics/thailand2007/930478778_8865df0cab.jpg   /home/pics/thailand2007/930478978_a24fe136ec.jpg   /home/pics/thailand2007/930479126_8769912df6.jpg   /home/pics/thailand2007/930479292_1085857e72.jpg   /home/pics/thailand2007/930480786_c96165de65.jpg  
/home/pics/thailand2007/930482944_54e28c165e.jpg   /home/pics/thailand2007/930483208_460de68cc8.jpg   /home/pics/thailand2007/930484210_ab23f570ca.jpg   /home/pics/thailand2007/930484562_5d1f408ac1.jpg   /home/pics/thailand2007/930485100_0e1f412a72.jpg   /home/pics/thailand2007/930485948_7532ef8195.jpg   /home/pics/thailand2007/930488018_27c65fe011.jpg   /home/pics/thailand2007/930714426_2999f06b1b.jpg   /home/pics/thailand2007/930718444_b8f686445c.jpg  
/home/pics/thailand2007/985411441_c71a05f1eb.jpg   /home/pics/thailand2007/986263094_fb87c59bdb.jpg  

Blog Entry #7

"Only Content controls are allowed directly in a content page that contains Content controls"

by Steven Monday, December 27, 2010 @ 1:35:00 AM

While I was working on my website, I was attempting to add a new content page to my site.  I love using MasterPages in whatever ASP.NET site I am currently developing on, so I have added literally hundreds of content pages to many sites.  I added a new content page called Photos.aspx.  I added a custom control for displaying photo albums, and I edited the page load to only show the most recent five albums added.  Fairly simple stuff.  I then ran the website and I got this build error:

"Only Content controls are allowed directly in a content page that contains Content controls"

<%@ Page Language="C#" AutoEventWireup="true" EnableViewState="false" CodeFile="photos.aspx.cs" Inherits="photos" %>
<%@ Register Src="User controls/PostList.ascx" TagName="PostList" TagPrefix="uc1" %>
<asp:Content ID="Content1" ContentPlaceHolderID="cphBody" Runat="Server">
    <div class="text">
        <div class="header">Photography</div> 
        <h1>Steven's Photography</h1>
        <p></p>
        <h1>Recent Photos</h1>
        <p>Below you will find the five most recent photo albums added to this site.  To see the full list, please go <a href="category/Albums.aspx">here</a>.</p>
    </div>
    <uc1:PostList ID="PostList1" runat="server" />
</asp:Content>>

It was my first time receiving this error, so I thought it had something to do with the custom control.  I made sure the registering of the control was ok, but I still received the error.  I then removed my custom code in the Page_Load, since I knew the control worked on other pages, but that didn't help either.  Finally I removed the control completely thinking that this had to solve it, but I still got the error. 

After doing a quick google search (something I probably should have done in the first place), I read some suggestions saying to look for code outside of the content control.  I quickly scanned my code again and found the problem:

</asp:Content>>

Such a simple mistake: an extra ">".  Is there a lesson to be learned from this?  When you get cryptic messages, look for typos first?  Or maybe, the right answer usually has the easiest solution?

Blog Entry #6

NKU Favorites

by Steven Sunday, December 26, 2010 @ 1:49:00 AM

Here are some of my favorite pictures from Northern Kentucky University (NKU).  I graduated from NKU with a Bachelor's in Computer Science.  It is a great campus that is very beautiful in spring and summer.  Most of these pictures were taken between 2004 and 2008.

/home/pics/favnku/IMG_0143.jpg   /home/pics/favnku/IMG_0483.jpg   /home/pics/favnku/IMG_0869.jpg   /home/pics/favnku/IMG_0884.jpg   /home/pics/favnku/IMG_0911.jpg   /home/pics/favnku/IMG_0921.jpg   /home/pics/favnku/IMG_0934.jpg   /home/pics/favnku/IMG_1167.JPG   /home/pics/favnku/IMG_1633.JPG  
/home/pics/favnku/IMG_1652.JPG   /home/pics/favnku/IMG_3374.JPG   /home/pics/favnku/IMG_6012.JPG   /home/pics/favnku/IMG_7757.JPG   /home/pics/favnku/IMG_7769.JPG   /home/pics/favnku/P1200112.JPG   /home/pics/favnku/P3020150.JPG   /home/pics/favnku/P4190682.JPG   /home/pics/favnku/P5170010.JPG  
/home/pics/favnku/P5170014.JPG   /home/pics/favnku/P5180022.JPG   /home/pics/favnku/P6210126.JPG   /home/pics/favnku/P6210130.JPG   /home/pics/favnku/P7130357.JPG   /home/pics/favnku/P7130359.JPG   /home/pics/favnku/P7130361.JPG   /home/pics/favnku/P7190450.JPG   /home/pics/favnku/P7190454.JPG  
/home/pics/favnku/P7200555.JPG   /home/pics/favnku/P8060035.JPG   /home/pics/favnku/P8120027.JPG   /home/pics/favnku/P8230033.JPG   /home/pics/favnku/P8230049.JPG   /home/pics/favnku/P9100051.JPG   /home/pics/favnku/P9210050.JPG   /home/pics/favnku/PB090146.JPG   /home/pics/favnku/Picture 019.jpg  
/home/pics/favnku/Picture 059.jpg   /home/pics/favnku/Picture 268.jpg   /home/pics/favnku/Picture 269.jpg   /home/pics/favnku/Picture 270.jpg   /home/pics/favnku/Picture 275.jpg   /home/pics/favnku/Picture 277.jpg   /home/pics/favnku/Picture 278.jpg   /home/pics/favnku/Picture 278b.jpg   /home/pics/favnku/Picture 281.jpg  
/home/pics/favnku/Picture 284.jpg   /home/pics/favnku/Picture 302.jpg   /home/pics/favnku/Picture 305.jpg   /home/pics/favnku/Snow_Lake_HDR2-8bit.jpg  

 

 

Blog Entry #5

BlogEngine.NET - Getting the total posts by author

by Steven Monday, November 15, 2010 @ 4:16:00 AM

I was creating a custom login panel for BlogEngine.NET where it would display some statistics after the user had logged in.  One of the basic stats was how many posts were created by this author.  I could not find any direct way to get the value, so I wrote a short function to calculate it:

    /// <summary> 
    /// Finds the number of posts by the specified author.
    /// </summary>
    /// <param name="author">The author's username.</param>
    /// <returns>Returns the total number of posts by the specified author or 0 if none found.</returns>
    public int PostsByAuthorCount(string author)
    {
        // Get a list of all public (viewable) blog posts.
        List<Post> posts = Post.Posts.FindAll(delegate(Post p) { return p.IsVisible; });

        // Iterate through the list and count ones by the specified author.
        int count = 0;
        foreach (Post p in posts)
        {
            if (p.Author == author)
                count++;
        }
        return count;
    }

And can simply call it like so to get the current logged in user's post count:

lblTotalBlogs.Text = PostsByAuthorCount(Page.User.Identity.Name).ToString();

You will need to include using references to System.Collections.Generic and BlogEngine.Core if you do not have them already.

 EDIT: After digging more with the code, I found a simpler way to get the post count by author:

List<Post> posts = Post.GetPostsByAuthor(author).ConvertAll(new Converter(delegate(Post p) { return p as IPublishable; }));

 

Blog Entry #4

BlogEngine.NET - Early impressions of BlogEngine.NET

by Steven Sunday, November 14, 2010 @ 1:27:00 AM

I have been working on customizing an instance of BlogEngine.NET, so I thought I would take this opportunity to write about some of the experiences of this process.  I was working on creating my own blog engine, and the process was quite rewarding.  I learned a lot.  It was much easier than I originally expected, but it was becoming more and more work as I thought of new things to implement.  I got a basic engine running that could post blogs, comments, star ratings, and some basic administrative features.  I then proceeded to add a code syntax highlighter, edit commands, sub-comments, a statistics page, an image-captcha, and more. 

It was a great learning process, however I soon realized this would take months to finish.  Then work diverted my attention for several months and I wanted to finish updating my website, so I began looking for blog engines in .NET (my primary language at work and at home).  I know I could have just downloaded a package like WordPress or signed up for a free blogging account, but I wanted to be able to heavily modify both the look of the blog and the code so it could integrate with the rest of my site.  After searching and reviewing several blog engines, I decided to try out BlogEngine.NET.

Installation was really simple.  It was literally up and running in just a few minutes.  One of the features I liked about this engine was that you could configure it to use multiple data sources, by default it uses XML to store the blogs, but it can be switched to SQL database in a few minutes.  Other features were also impressive.  It used the same code syntax highlighter I was already familiar with, and it used TinyMCE for the editing of blogs - another great free open source tool.  The dashboard made managing and customizing the blog settings a breeze.  The user interface was also quite nice.  Another (hopefully) great feature is all of the extensions for BlogEngine.NET - there are lots out there, but I have yet to try any besides the included default ones.

There were a few downsides to it, but none of them were crucial to me.  One issue I had was with inserting images.  It is not as easy or as flexible as I would like - you are very limited to how you can insert images.  Also, since I like taking a lot of pictures, I was hoping some kind of gallery feature would be integrated as well.  There is currently an incomplete extension for it (http://blogenginephoto.codeplex.com/), but it seems like development on it has stopped.  I'll therefore have to wait or write my own. 

My biggest downside has to be customizing the look of the site.  There is some basic documentation on creating themes, but they are far from getting into the many issues I have run into so far.  To truly create a unique look, you will have to read a lot of the code to understand how it works and where values are stored.  I can't complain about this too much, but the best documentation tends to be other people's blogs (a bit ironic).  Hopefully my coming blogposts will help others as well.

If you want a good blogging engine, then I think this one is great.  If you plan to customize the look or features much, then you should be pretty familiar with ASP.NET web development.

Blog Entry #3

Removing HTML Tags with RegEx

by Steven Sunday, November 7, 2010 @ 8:05:00 PM

When creating my own little blog engine, I have run into quite a number of unexpected bugs, but I guess that is the joy of programming, right? One of these bugs was on the main blog page where I summarize some of the recent blog entries, and I trim the text if it is over X characters long and append a 'Read More' link if it this happens. This worked perfectly fine until a recent blog entry had the 'Read More' link appended in the middle of some HTML tags, which caused the link to break.

I then had to find a way to strip HTML tags before appending the link, so I did so using a regular expression I found from Roy Oshervos's blog:

    /// <summary>
    /// Trims a blog entry for displaying a starting summary of the blog without breaking the code in the middle of a link or other HTML tag.
    /// </summary>
    /// <param name="text">The blog content to trim.</param>
    /// <param name="id">Blog ID for linking to the blog.</param>
    /// <param name="max">Maximum length of the blog description.</param>
    /// <returns>Returns the blog trimmed to the max length if over.</returns>
    private string TrimEntry(string text, string id, int max)   
    {   
        string entry = "";   
        
        // Check if the length is under the allowed limit.   
        if (text.Length < max)   
            entry = text;   
        else   
        {  
            // Remove HTML tags to prevent from inserting link in middle of tags.
            entry = Regex.Replace(text, @"<(.|\n)*?>", string.Empty);  
            
            // It is over, so trim the entry and add a 'Read More' link to it.  
            entry = entry.Substring(0, max);  
            entry += "... <a href='blog.aspx?viewblog=" + id + "'>Read More</a>";  
        }  
        return entry;  
    }

Pretty simple to fix, although I could see extending it more for larger blog engines. Such as detecting if the link will be in the middle of tags, if so, put the link before the tags and delete the rest after it. But for my needs, this was a quick and simple fix that I hope others can find useful as well.

 

This page was last updated on January 2, 2011 (749 bytes).