About Me, Projects, and Tag Index updated

So I actually got around to writing code on my website for about an hour and now I have pages I can actually edit without recompiling. So, be prepared for an actual Projects page, and for a lot more updates to happen to the About Me page.

Also, there is now an actual tag index. Check it out. I think it's cool anyway.

Also, be prepared for less hacking on the NVG510 because I actually have some free lance work to do. So I'll try to limit myself to hacking on it during the weekends and late at night.

Tags: meta updates
Posted: 6/11/2012 7:40:17 AM

But how does it work?!

I was going to put a rant in here about PHP sucking, but instead I'm going to do something useful and informative. Describe how this blog works.

When I started this blog, I had only used ASP.Net with WebForms. Even though I didn't know anyway to do it better, I could see the shortcomings of pretending HTTP is a stateful protocol. This is because before ASP.Net, I used PHP, which actually taught me quite a bit about the HTTP protocol. I've since abandoned the PHP ship due to stuff like this, but that's another story.

I believe at the time ASP.Net MVC was still pre-release or still filled with bugs, especially for Mono. I was set on using Linode for my hosting because I've never had any problems with them, and I prefer managing Linux servers to Windows... So I had a choice, Use WebForms, or use a different framework. I really like C# though, so I instead created a very minimalistic framework.

This framework eventually got broken up into 3 different pieces of software.

  1. EView -- a powerful, yet simple, static view generator written completely in T4
  2. EFramework -- a routing engine and basic replacement for using ASP.Net MVC or WebForms
  3. FSCAuth --a very easy to use Authentication library

At first, these were all one framework named EFramework, but I since broke them up since they are all useful by themselves as well.

So first up, I'll explain how EView generates everything you see here.

EView is just a single T4 file. I would've broken it up into separate files but at the time Mono had a bug that prevented me from doing it easily. How it works basically is you place EView into a directory, and when you run the T4 file template, it'll read all the files from the current directory and generate a C# file.

So, let's do a simple example. You have a folder named Templates and 3 files, EView.tt (the T4 file), IndexView.html, EntryView.html

So, you run EView.tt. Visual Studio (or MonoDevelop or whatever) runs the T4 template during design time. EView will scan the folder it's placed in. It finds IndexView.html and EntryView.html

Let's say IndexView.html contains this:

<!DOCTYPE HTML>
{@
Entry as IEView;
@}
<html>
<head>
    <title>Test Index</title>
</head>
<body>
  <div class="entry">
    {=Entry=}
  </div>
</body>
</html>

Now let's explain. What's that weird {@ thing at line 2? This is an EView directive. EView has blocks like { } The symbol after the { and before the } indicates what the directive is. The @ directive is for variable declarations. So basically, in the view class it generates, it'll add the public variable Entry of type IEView to it. IEView is the interface that all EView views implement. After that it looks like plain HTML until {=Entry=}. The {= directive tells EView "put this variable's value here. So it wants to put theEntryvariable there. EView is smart enough and knows that Entry is a View, so it callsEViewRender at that point.

So basically, this all generates a view class like this: (more complicated than this, but to be easy to understand, this example is simpler)

  public class IndexView : IEview
  {
    public IEView Entry;
    public string EViewRender(){
      StringBuilder sb=new StringBuilder();
      sb.Append(@"""<!DOCTYPE HTML>
      <html>
    <head>
        <title>Test Index</title>
    </head>
    <body>
      <div class=""entry"">
     """);
     //insert {=Entry=}
     sb.Append(Entry.EViewRender); //a lot more logic goes into this, but just for example sake
     sb.Append(@"""
      </div>
    </body>
      </html>
      ");
      return sb.ToString();
    }
  }

And that's basically how it works. So when you want to use IndexView, you just do this:

var index=new IndexView();
index.Entry=MyEntryView; 
string html=index.EViewRender();

So as you can see, it's very powerful really, while views are really simple to create. No weird XML tags to use, or other crao. You just write your HTML and use a { } blocks to describe how the view should be dynamic. It supports many more operations than shown here such as if statements, foreach statements, and a special Layout directive that works similar to ASP.Net's MasterPages.

So now you understand a bit of what my views look like and how they're rendered, we can move on to EFramework.

EFramework was really an experiment to see if what I was trying to do was even possible.. and then I thought it worked really well, so I kept at it.

What exactly does EFramework provides? It provides 2 things:

  1. A powerful routing engine
  2. An HttpHandler class that functions similar to ASP.Net's .ashx Handlers.

So let's start with the routing engine. The routing engine has 3 pattern matching engines

  1. Regex for regular expressions
  2. Plain for exact plain text matches
  3. Simple, used for my own pattern matching that resembles ASP.Net MVC's routing URLs

I'll only cover the Simple type of pattern matching.

Here is the Routing record for the pages of my Blog:

Routing.AddRoute("paged_index",PatternTypes.Simple,"/blog/{page}",()=>{return new BlogHandler();});
  1. "paged_index" is the name of the route, which is passed to the HttpHandler that handles the route.
  2. PatternTypes.Simple is which type of pattern matching
  3. "/blog/{page}" is the pattern to match. It basically says match anything that looks like /blog/* where * is anything, and put the wildcard value into the page route variable
  4. This is a lambda for creating a new HttpHandler. So basically, when a route matches, it will call on this Lambda and expects for the lambda to return an HttpHandler of some sort

The Simple pattern matching is also quite powerful and can do more than simple wildcards. It's aware of how / affects URLs and can do the following:

  1. "/blog/{entryid}/comment/add" This will match anything like /blog/*/comment/add where * can be basically anything but / and when it reaches a / it knows the wildcard is over
  2. "/test3/{id}/{action=[view,edit]}/{*}" This one is a bit more complex. The first {id} of course matches anything up til the next /. {action=[view,edit]} indicates that this wildcard can only be view or edit. If it is not one of those two, the route doesn't match to the URL. And finally, the {*} adds support for "descriptive" URLs, such as from StackOverflow: http://stackoverflow.com/questions/2185781/why-cant-c-sharp-compiler-follow-all-code-paths-through-a-switch-statement

That's about as complex as it gets right now. I'm sure there are some URLs it can't accept, but it works for most URLs I've seen.

Now then, how does an HttpHandler work exactly?

public class BlogHandler : HttpHandler
{
  public override void Get ()
  {
    if(RouteID=="page"){
      var v=new IndexView();
      v.Entry=GetEntry(RouteParams["id"]);
      Write(v.EViewRender());
      return;   
  }
}

Pretty simple. The Get function is called when the HTTP method is GET, Post for POST and etc. RouteParams is a dictionary with all of the route variables in it. So in this case, the route probably looks like

Routing.AddRoute("page",PatternTypes.Simple,"/blog/{id}",()=>{return new BlogHandler();});

And that's for the most part, what makes EFramework good enough for me.

And then there is FSCAuth, what prevents all of you from posting articles on my blog.

FSCAuth was made because I think ASP.Net's authentication mechanisms are a bit too complex for simple authentication scenarios, such as this blog. And FSCAuth is so easy to use and so secure(by default) that I decided to try to sell it. So how does FSCAuth tie into this site?

Well, I use MongoDB for my database (also mainly for learning something new :) ). FSCAuth works by having a UserStore class and a few configuration options set in Global.asax

First the easy part. My configuration looks like this:

        Authentication.UserStore=new MongoUserStore(); //set the UserStore to be used
        Authentication.SiteName="Last Year's Wishes"; //put into login cookies
        Authentication.LoginPage="/login"; //where to redirect to when authentication is required
        Authentication.UniqueHash=Config.UniqueHash; //our secret hash
        Authentication.CookieHttpOnly=true; //Set HttpOnly property on cookies
        Authentication.CookieSecure=false; //set to choose to only transport cookies over HTTPS 
        Authentication.CookieUseBase=true; //hash the physical path of the application into cookies. 
        Authentication.CookieUseBrowserInfo=false; //hash in client's browser information to the cookies. 
        Authentication.CookieUseIP=true; //Hash in the client's IP address to the cookie. Ties the cookie to the client's IP 

Now the last configuration thing to do is create our UserStore. This is a direct rip of mine (to show how easy it is to create one)

public class MongoUserData : UserData{
    [BsonId]
    public ObjectId ID{get;set;}
}

public class MongoUserStore : IUserStore
{
    public MongoUserStore ()
    {
    }
    public UserData GetUserByName (string username)
    {
        var db=Config.GetDB(); //gets our MongoDB database instance
        var u=db.GetCollection<MongoUserData>("users").FindOneAs<MongoUserData>(Query.EQ("Username",username));
        return u;
    }

    public bool UpdateUserByID(UserData user)
    {
        var userdata=(MongoUserData)user;
        var db=Config.GetDB();
        var u=db.GetCollection<MongoUserData>("users");
        if(u.Save<MongoUserData>(userdata)==null){
            return false;
        }else{
            return true;
        }
    }

    public bool AddUser (UserData user)
    {
        var userdata=(MongoUserData)user;
        var db=Config.GetDB();
        var u=db.GetCollection<MongoUserData>("users");
        if(u.Find(Query.EQ("Username",userdata.Username)).Count()!=0){
            return false;
        }
        u.Insert<MongoUserData>(userdata);
        userdata.UniqueID=userdata.ID.ToString();
        u.Save<MongoUserData>(userdata);
        return userdata!=null;
    }
    public bool DeleteUserByID(UserData user){
        var userdata=(MongoUserData)user;
        var db=Config.GetDB();
        var u=db.GetCollection<MongoUserData>("users");
        u.Remove(Query.EQ("_id",userdata.ID));
        return true;
    }
}

As you can see, it's not too complex to create... unlike a similar thing for tying ASP.Net's Forms Authentication to MongoDB.. I know it can't be done in only 58 lines of code anyway.

So then, how to actually make use of FSCAuth, all I do is call Authentication.RequiresLogin() or one of the other Authentication functions.

This is my actual Post method for my BlogHandler:

    public override void Post(){
        if(RouteID=="new"){
            Authentication.RequiresLogin();
            var c=Config.GetDB().GetCollection<BlogEntryData>("entries");
            var entry=new BlogEntryData{
                Title=Form["Title"],
                Text=Form["Text"],
                Publish=Form["Publish"]!=null,
                Posted=DateTime.Now,
                Tags=new List<string>(Form["Tags"].Split(new char[]{' '},StringSplitOptions.RemoveEmptyEntries))
            };
            c.Save<BlogEntryData>(entry);
            Response.Redirect("/blog/view/"+entry.ID.ToString());
        }
        if(RouteID=="edit"){
            Authentication.RequiresLogin();
            var c=Config.GetDB().GetCollection<BlogEntryData>("entries");
            var entry=c.FindOneByIdAs<BlogEntryData>(new ObjectId(RouteParams["id"]));
            entry.Title=Form["Title"];
            entry.Text=Form["Text"];
            bool p=Form["Publish"]!=null;
            if(!entry.Publish && p){ //if not previously published, update the time so it's "bumped"
                entry.Posted=DateTime.Now;
            }
            entry.Publish=p;
            entry.Edited=DateTime.Now;
            entry.Tags=new List<string>(Form["Tags"].Split(new char[]{' '},StringSplitOptions.RemoveEmptyEntries));
            c.Save<BlogEntryData>(entry);
            Response.Redirect("/blog/view/"+entry.ID.ToString());
        }
    }

And that's all I have to do. Super simple.

Even though I created this framework as a workaround because MVC at the time was very experimental with Mono, I found that I rather like my framework. I can see quite a few limitations, but it works wonderfully for small scale websites. I also have not measured the performance of any of this except for FSCAuth, so I'm sure it's not enterprise ready, but for this low-traffic blog, it works really well. And I learned a lot about ASP.Net in building it.

Anyway, hope you enjoyed reading how this site works and maybe are curious about one of my 3 projects. In case you are, here are the links to the projects again:

  1. EView -- a powerful, yet simple, static view generator written completely in T4
  2. EFramework -- a routing engine and basic replacement for using ASP.Net MVC or WebForms
  3. FSCAuth --a very easy to use Authentication library
Posted: 5/3/2012 8:16:16 PM

Yay Comment Support Yay

I finally got around to adding comment support to this website after just a tiny bit of frustration (Thank god for Linode's backup service).

Anyway, the comment support is pretty basic. Just HTML encoded text. No links or anything, though I do eventually want to support a basic version of Markdown... Also, we'll see if my spam preventer is good enough. I kind of doubt it is, but I have a kill switch if I get flooded with spam.

So go on, test it out and see if it works!

Edit:

Also, yes, I plan on fixing the comment links on the front page to actually align. That didn't show up in my test environment. Right now It's 1:30am though and I'm going to sleep since this update took much longer than I wanted

Tags: meta comments
Posted: 3/16/2012 6:22:56 AM

New Look

Well, I got a new look thanks to Nate Ernst over at Varloo Design

I rather like the new look he gave. I got it by submitting a small little project to Weekend Hacker. I suggest everyone try it. It's pretty nifty.

Posted: 7/4/2011 10:04:26 PM

Sup 86.122.189.166

So I've been recently attempted to be brute forced by the IP 86.122.189.166. The little fellow tried to login to root a good number of times. He has SSH enabled on his machine, so I've considered trying to do the same to him.. but instead, I'll just add an entry to hosts.deny and get on with my life

Tags: meta stupid ban
Posted: 6/23/2011 10:20:04 PM

Old stuff

So I imported some of the better posts from my old blog that ran SLIBE. You can read those by searching the tag old-import. Note, some are a bit rough. I had to import by hand, so most have no tagging and as little conversion as needed to be readable.

Tags: meta
Posted: 5/20/2011 1:10:32 AM

Tags are a go

Tags finally work. Well, I mean the /tags/{tag}/{page} mechanism. The tag index is still blank, but you can now click the tags below this post and see everything tagged meta and what not.

Also, updated to using FSCAuth as the authentication here. It was using it before, but it use to be part of "EFramework". As such, that version was a bit cumbersome in some areas, though the core authentication algorithms haven't changed since I first wrote it.

Also, you may see "work in progress" posts. I'll work on an extra option to hide those, but basically you'll be able to see both blog entries and pages by looking through tags

Posted: 4/23/2011 4:54:32 PM

Umm. Well that wasn't expected

I recently had a bit of a problem when doing a tiny bit of maintenance on my MongoDB database. Luckily, I had backups. Unluckily, they were very old. Luckily again, I had just subscribed to Linode's wonderful backup service. Unluckily, this meant I had a bit more maintenance to do than just copy a set of files.

So now we're back up. And I finally got the feature I've been trying to deploy since last night. Paging! Also, a few minor color differences.

A word of advice: Always do database updates in a staging database to be 100% sure of it's results before doing it on production. And if in doubt even then, take a backup. Hopefully, you won't thank me later

Posted: 4/20/2011 11:20:22 PM

Page support

Well, I finally got page support. Basically, they're ugly and only function as pages utilizing markdown and they function like a blog entry that isn't put on the front page. Nevertheless, they're cool

and just to show off, here's one

Note to self: implement friendly URLs extremely soon, along with find beta testers for FSCAuth

Tags: meta
Posted: 4/14/2011 4:01:59 AM

Test page

Wheeeee!

Tags: test meta
Posted: 4/14/2011 4:00:03 AM