Run VS in admin mode on Win7 for remote access

This is really one of those duh kinda things, but I thought I’d just put it up here in case it helps at all.

Once I made the switch to Win7 on my work PC, I noticed when I remote desktop’d in I could not run the debugger for an already open VS instance without shutting down and starting as admin. I noticed in some screencast from people such as Phil Haack that the VS window had “(Administrator)” in the title bar. This made a little light bulb appear over my head as I had an “aha!” moment: just run VS as an admin.

I had already pinned VS to my taskbar, so I stopped for a second when I right-clicked on the VS icon and saw no “Run as administrator” option. I quickly realized I could right-click yet again on “Microsoft Visual Studio 2010” fromt he jump menu and lo and behold there was my “Run as administrator” option.

SVN Tree Conflicts

One thing that has always baffled me and my co-workers is SVN tree conflicts. I thought it would be a good idea to keep track of different scenarios that result in tree conflicts as an exercise in demystifying them.

  • Delete a file but don’t check in the delete. Another user performs the same delete and checks it in. Perform an update = tree conflict

Automating TortoiseSVN

So the whole reason I started this stupid blog was so I’d have a place to fall back on with all the interesting crap I found on the interwebz. Yeah, that worked out well…

So why the hell am I not just adding every cool thing I find?!? Because it is way easier to bookmark it. Hell, I use this pre-alpha version of Oxite and I gotta type in my HTML markup by hand. How archaic is that! [Edit: not any more!]

Well, here is a great link to TortoiseSVN automation. I have been working towards completely automating our release procedure, and we always had the Tortoise dialog that we had to click “OK” for every time. We all know how completely annoying that can be, right? Well, no more!

Example

What good is this post without some sort of example, right? Well here you go.

We used to do something like so:

"C:\Program Files\TortoiseSVN\bin\TortoiseProc.exe" /command:update /path:%FOLDER%

That has the annoying dialog. So now we just do the following:

"C:\Program Files\TortoiseSVN\bin\TortoiseProc.exe" /command:update /closeonend:2 /path:%FOLDER%

That handy little /closeonend saves my click-finger! Voila! The post goes into detail on the parameter, so have a look.

SVN post-commit trigger

We use a post-commit trigger at work to notify the team of all commits made to the repository. I needed to make some modifications, so I thought I’d update my sorely neglected blog with the details. You know, for the kids…

Original post-commit file

This isn’t the exact file we use, but the majority of it was taken from this post. Dan has a great file that you can pretty much drag ‘n drop and have some seriously cool emails flying around. As Dan notes, you’ll need SendMail, but it is practically an out-of-the-box solution.

Recent tweaks

I get email. A LOT of email. It is a bit hard to manage, so I got rules up the wazoo! I originally had all the commit emails simply going into my “Commits” subfolder. However, we have some different apps, and a few different web sites going on, so having all the notifications dumped into a single folder wasn’t working either. Dan’s script adds some useful information to the subject line, but it wasn’t quite what I was looking for. I was trying to use keywords from the body of the email, but that wasn’t 100% either. So, I had to make some tweaks to get what I wanted.

I wanted to add the repo folder that was updated to the subject line. This way, I could write my rules to look for keywords based on the folder names of our projects in the repo. This would be fool-proof. Batch files have always been a little odd to me, and some of the deep, dark recesses of DOS are left unexplored. I did some searching and came up with this StackOverflow question about getting details of what was committed via svnlook. Using the dir-changed along with the suggested for loop worked quite well. Now all I had to do was get a part of the dir-changed.

More searching turned up this and this, which are both essentially the same. They pointed me down the right path and I got it going.

Not quite perfect

I am totally anal. I have personally labeled myself a “Code Nazi”. I can’t stand extra spaces, or extra line breaks, or anything that is seemingly unnecessary. It seems that svnlook author must add some extra spaces to the end of the author name, because there were 3 extra spaces at the end of every name. Up until now, I decided to just let this go. I mean, really, it wasn’t like it made the subject line illegible or anything. But, I love Mike Holmes, and like he says, make it right! So more searching turned up this great page on DOS string manipulation. I used it to replace the spaces in the author name and now the subject line is perfect!

The script

After all this talking, I could at least share the code, right? As I said earlier, it is heavily borrowed from Dan’s great code, with some minor updates made for my taste. Here you go:

@ECHO OFF
REM *************************************************************
REM * this sets the arguments supplied by Subversion            *
REM *************************************************************
SET REPOS=%1
SET REV=%2

REM *************************************************************
REM * define the path to the working copy of your code          *
REM *                                                           *
REM * the default working_copy folder would be:                 *
REM * file:///%REPOS%                                           *
REM *************************************************************
SET WORKING_COPY=file:///c:/web/svnroot
SET DIR=%REPOS%/hooks

REM *************************************************************
REM * get author's name                                         *
REM *************************************************************
for /f %%a in ( 'c:\svn\svnlook.exe author %REPOS% -r %REV%' ) do ( SET AUTHOR=%%a )

REM *************************************************************
REM * define e-mail parameters                                  *
REM *************************************************************
SET SITENAME=emw2k8dev
SET SMTP_SERVER=192.168.0.112
SET EMAIL_TO=svn@evan-moor.com
SET EMAIL_FROM=svn@evan-moor.com
for /F "eol=; tokens=1 delims=/" %%a in ( 'c:\svn\svnlook.exe dirs-changed %REPOS% -r %REV%' ) do ( SET SITENAME=%%a )
SET SUBJECT=SVN Update - %AUTHOR: =% - %SITENAME% - rev %REV% - \\emw2k8dev\%REPOS:C:\=%

REM *************************************************************
REM * generate the header to use for the e-mail message         *
REM *************************************************************
ECHO The following changes were made to the code: > %DIR%/email.txt
ECHO. >> %DIR%/email.txt

REM *************************************************************
REM * dump the log of changes to the e-mail message             *
REM *************************************************************
c:\svn\svn log %WORKING_COPY% -v -r "%REV%" >> %DIR%/email.txt
ECHO. >> %DIR%/email.txt

REM *************************************************************
REM * dump the diff changes to the e-mail message               *
REM *                                                           *
REM * WARNING: Generates tons of output                         *
REM *                                                           *
REM * NOTE: this is optional, you can remove this information   *
REM *       if you do not want a verbose message of changes     *
REM *************************************************************
c:\svn\svn diff %WORKING_COPY% -c "%REV%" --no-diff-deleted >> %DIR%/email.txt
ECHO. >> %DIR%/email.txt

REM *************************************************************
REM * send the e-mail message to the user                       *
REM *************************************************************
c:\web\svnroot\sendEmail.exe -s %SMTP_SERVER% -t %EMAIL_TO% -f %EMAIL_FROM% -u "%SUBJECT%" -o message-file=%DIR%/email.txt -l %DIR%/SendEmail.log

So it is by no means perfect or anything, but it may come in handy to someone else.

jQuery Star Rating Plugin

I am thinking about adding a star rating to one of the sites at work, so I started poking around for what was available in a jQuery plugin. Surprisingly, there are quite a few plugins but none that I totally fell in love with. they either don’t degrade, or use form elements I don’t hink they should, or whatever. I thought it would be a good exercise to try and roll my own plugin.

I started of checking out some how-to’s for jQuery plugin writing, but I usually learn better by doing, so I started with the closest thing I like, Wil Stuckey and John Resig’s rating plugin. It is pretty good, but it just needed a little nudge to be better. I figured this was a good way to start. I would not have to write an entire plugin from scratch, and I’d learn from one of the guys that created jQuery, so how could I go wrong?

It took a few days, with a few hours spent here and there. All in all I feel pretty good about the plugin, though I am sure I could make it better, and I likely forgot something important. If you are interested, you can grade the code from my code section, and there is a demo available too. Let me know what you think in the comments.

Javascript beautifier

I am looking into a jQuery star rating plug-in and I cannot seem to fins one that fits exactly what I want. There are a few out there, but many require javascript, and those that do degrade with js off aren’t exactly what I want.

I stumbled on starbox, but it is for Prototype. It looks really nice and I thought it might be fun to try and port an existing plug-in. I’d get something I like and get some good jQuery experience in the process, which is a win/win in my book. The problem is starbox isn’t a freebie, and being the cheapskate that I am I don’t want to pay for it when I am really not going to use it (other than for the porting). Starbox is packed, so I needed an easy way to unpack the code, and I figured it might be tough without paying for it.

I did a quick search and found a way to use the FF error console to unpack code, but I also found the online javascript beautifier. This little nugget will unpack and format packed js code. Pretty sweet stuff, and just what I needed.

In the PC market? Do NOT buy an HP!

Less than 2 years ago I needed a new laptop. I went with an HP mainly for the following reasons: 1) it was from Costco so the warranty was longer than the typical warranty, and 2) it was one of the only ones in my price range at the time. When I bought this laptop, it was about $900. Right now I could get an equivalent laptop for probably $350, but oh well.

I like laptops for the portability. I travel occasionally and I love to be able to bring along my PC. Another frequently used feature of any laptop is opening and closing it. A few weeks ago I closed my laptop and a small plastic piece broke off. As I mentioned, I travel some, but I take great care of my laptop. Mostly it just sits on my desk, open and ready to go. So the fact that something would break off when I closed it irked me a bit. It was a small piece, so I wasn’t worried.

When I opened it the next day, another piece broke off. Now I was a bit concerned. I opened and closed it again to see if anything else would happen, and basically the entire hinge broke. Now, I couldn’t even close the laptop properly. My laptop is still under warranty, so I contacted online support and told them the problem. They said I was covered under warranty and that a box would be sent. A couple days later, I get the box, pack up my PC and send it in.

Now it is a week or so later, and I get a few calls from HP. Their service hours were not working well with my work hours so I was unable to contact them for a few days. Finally, when I got them on the phone they said the hinge wasn’t covered under the warranty and that it would cost me somewhere in the neighborhood of $420 to fix it?!? For a hinge? I told them to forget it and send it back. I was so angry I couldn’t even argue with them at that point.

About a week goes by and I finally receive my laptop back. When I open it I find HP’s “standard” customer refusal form included. I think any company that has a standard form has lots of practice screwing their customers. Anyway, the reason selected for not covering my laptop under warranty is that electronic parts won’t be covered under warranty if they have had liquid spilled on them. Huh? The PC is a bit slow for my taste, but totally operational. I couldn’t let this BS fly, so I called in to dispute this rationalization.

Long story short, I get hung up on by a case manager (nice customer service!), and my subsequent call to ask if hanging up on customers is common HP practice yielded a very enlightening conversation with another case manager. HP gives their service reps no capabilities to override anything, thus eliminating their ability to actually help customers. The reps cannot give out any contact information for the tech service department (which makes the decisions to not cover items under warranty), and no help with finding out how to dispute anything. This effectively allows HP to operate much like an insurance carrier. I guess they figure if they give customers the run-around enough they’ll go away? Normally, I would, but I was quite irate with the situation. What is the point of a warranty if the company that makes the item won’t fix/replace normal wear and tear? I could have pumped ridiculous voltage through my PC and fried everything and they’d replace it, yet they won’t fix a hinge that broke because I opened and closed my laptop?!? Next thing you know I’ll want to turn it off an on…

I started doing some web searches, and it turns out this must be common practice. I found a Yahoo! Answers question from someone in the exact same situation as myself. I found other stories of how HP help product until the warranty expired then attempted to charge the customer, and I read yet another story of HP claiming Amazon sold a woman a fake PC with a mysterious motherboard.

Ultimately I found contact information for a VP of Customer Service at the headquarters in Palo Alto, CA. I sent an email and actually received multiple email responses and numerous calls. Now, I have been on vacation for the past week, so nothing has come of it, but I imagine in the end it’ll be the same old, same old. They won’t cover the repair and I’ll tell them again, as I am telling you and everyone else I know: I WILL NEVER BUY ANOTHER HP PRODUCT AGAIN. EVER.

Even if they offer to fix it, I am going to tell them it is too little, too late. I went on eBay and bought 2 hinges (1 for each side) for less than $20 shipped! They wanted me to pay $420, and I got the parts for less than $20? No thanks, HP. I’ll buy from a company that stands behind their products and doesn’t try to screw their customers.

One other thing: I contacted Costco and told them of my story. I told them if the continue to sell HP products they’ll be losing my business. The service rep apologized for not being able to do anything, but said she was glad I called to voice my displeasure since Costco would never know any better if I didn’t. So, if you have ever had issues with HP (or anyone else for that matter) don’t let them walk on you. Tell everyone. tell the store you bought it from. Start a blog just so you can tell the interwebs. Speak with your wallet. Scream it from rooftops. Whatever…

Just know you don’t have to take it.

If you are interested, here is my email I sent to the VP:

From: Tim Hobbs [mailto:xxx@xxx.xxx]
Sent: Friday, August 07, 2009 8:53 PM
To: ‘daniel.bulnes@hp.com’
Subject: Poor support and poor business tactics

Mr. Bulnes,

Unfortunately, my experience with various support reps (including the case manager that hung up on me) has set the bar rather low. Perhaps that is fortunate for you as I fully expect the “company line” from you as well. Anyway, her goes…

The right hinge on my Pavilion dv9000 series laptop recently broke. It simply crumbled under normal use (most likely due to a defect in materials or workmanship). I contacted live chat support, was told I was covered under warranty for a broken right hinge, and sent a box with FedEx shipping prepaid.

Per the email below, I was told that the hinge I was not covered under warranty and that it would cost approximately $420 to replace the hinge. I believe the retail of the laptop was $899. I am expected to pay almost %50 for a part to be replaced. Ridiculous. I told the rep to send it back since I was too furious to try and reason with him.

Today when I finally received my laptop I found the HP_NB_Refusal_of_Repair_Service_RevB standard company form in the box. The reason for refusal was electrical components won’t be covered under warranty if liquids are spilled on them. Explicitly stated on the form is the following:

“Your HP warranty protects you against defects in materials and workmanship.”

Feeling cheated I called the number provided on the form for “Award-winning HP Total Care”. I had 2 nice reps and waited about 20 minutes before I could speak to a case manager. As I told him of my frustration, he proceeded to ask me if I read the warranty when I bought the PC. Anyone who expects users to read warranties and/or user agreements is a fool, and I resent the implication that it is my fault that I did not read the fine print when the issue is an obvious materials issue. Admittedly I did lose my cool a bit and raised my voice (which isn’t acceptable), but what is even more unacceptable is that without any warning the case manager hung up on me. I expect that isn’t part of the “Award-winning” experience.

I immediately called back and just happened to get a manager that answered. He was  very understanding and helpful, and basically told me exactly what I was told before, and what I have subsequently read repeatedly on various internet sites. In the end, I hope for the best. I hope that you will recognize the poor customer service. I hope you will fix the hinge on my laptop. However, I doubt it will happen. If you are interested in making things right my case number is included below.

What I know is that I will never spend another dime on an HP product again. (I am a geek. I buy a lot of gadgets.) I will tell everyone I know about my experience. I will make sure when my employer buys new hardware for the office that they are never HP (I am in the tech department so it isn’t just an empty threat). I am my family’s tech guru, so they won’t be buying HP either.  I’ll post it on my blog and comment on others. I’ll tell everyone I know to avoid HP. Individuals  and businesses alike cannot rely on HP to back their products and do what is right, so they can’t afford to buy HP if they value their money.

Sincerely,

Tim Hobbs

My first foray into ASP.NET MVC

I have recently begun the .NET MVC journey, and I see recurring “buzzwords”: Inversion of Control (IoC) and Dependency Injection (DI). If you are at the same point in your MVC journey as me, you may be wondering what the heck these things are?!?! They have certainly caused me a bit of confusion. I have yet to implement DI, but I have decided to stick with the MS stack and try out Unity.

I decided I’d give this whole thing another shot tonight. I recently built my first little MVC application – a guestbook. Remember those from 1996? I thought it would be something a bit different and I knew it would be pretty simple (the database is two tables, 1 of which I am really not using right now). I also brought it into the 21st century with a little Google mappage to show where the guestbook signer is located on our big, blue marble. I’ve got it mostly working and I’ll get it up online as soon as I get the membership stuff locked down.

I want to “do it the right way”, so I want to try out the DI stuff. I have been searching for a good starter post that would break down the whole IoC/DI stuff into small, digestible chunks that my overworked brain could handle. I finally found .NET HITMAN’s post that breaks down the history behind Unity, why DI is a good thing, and how to add references to the Unity DLL’s to your project, which is exactly what I was in need of. Since this is an ASP.NET MVC app, I also found Shiju Varghese’s post that explains how to add DI to the NerdDinner project. Since I “followed along” with the NerdDinner app while creating my Guestbook app, I found the post extremely helpful and easy to follow.

When I started off with my MVC project, I went with SubSonic 3.0 for data access. I am a big fan of SubSonic and I use 2.1 at work, so I was really interested to see what 3.0 had to offer and I knew that with Rob’s focus on MVC that 3.0 would work quite nicely. It also gave me a chance to use some LINQ goodness, which, to this point, I haven’t been able to use extensively. I was quite impressed with the T4 LinqTemplates and overall I think 3.0 is a great new addition to the SubSonic family.

Another thing I keep seeing in MVC apps is repositories. It is my understanding that the repository enables easier unit testing (but I am an .NET MVC n00b, so I could be wrong) since the controller isn’t talking directly to your data access code. So I went about creating a GuestbookRepository class for my controller to talk to.

Now that I am going the whole DI route, it seems I need to first create an interface, then inherit from that interface. So here is the IGuestbookRepository interface:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
using MvcApplication.Models;
 
namespace MvcApplication.Repositories {
 
    public interface IGuestbookRepository {
 
        IQueryable<mvc_guestbook> GetAllMessages();
        mvc_Guestbook GetMessage(int id);
        void Add(mvc_Guestbook guestbook);
        void Delete(mvc_Guestbook guestbook);
        void Save(mvc_Guestbook guestbook);
        void Update(mvc_Guestbook guestbook);
    }
}

* NOTE: I don’t really like the mvc_Guestbook references in there (they seem smelly) but I am not sure what I should put there, so this is going to have to do for now until I determine if this is correct or find out what is correct.

Now that I have my interface, I modifed my GuestbookRepository class to inherit from the interface. Here is the code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
 
using MvcApplication.Models;
 
namespace MvcApplication.Repositories {
 
    public class GuestbookRepository : IGuestbookRepository {
 
        private GuestbookDB db = new GuestbookDB();
 
        public IQueryable<mvc_guestbook> GetAllMessages() {
            return from guestbook in db.mvc_Guestbooks
                   orderby guestbook.MessageDate descending
                   select guestbook;
        }
 
        public mvc_Guestbook GetMessage(int id) {
            return db.mvc_Guestbooks.SingleOrDefault(g => g.GuestbookID == id);
        }
 
        public void Add(mvc_Guestbook guestbook) {
            guestbook.Add();
        }
 
        public void Delete(mvc_Guestbook guestbook) {
            guestbook.Delete();
        }
 
        public void Save(mvc_Guestbook guestbook) {
            guestbook.Save();
        }
 
        public void Update(mvc_Guestbook guestbook) {
            // Save message history
            SaveHistory(guestbook.GuestbookID);
            // Save updated message
            guestbook.Update();
        }
 
        private void SaveHistory(int id) {
            mvc_Guestbook past = mvc_Guestbook.SingleOrDefault(g => g.GuestbookID == id);
            mvc_GuestbookHistory history = new mvc_GuestbookHistory();
            history.GuestbookID = past.GuestbookID;
            history.Name = past.Name;
            history.Email = past.Email;
            history.Message = past.Message;
            history.Location = past.Location;
            history.Latitude = past.Latitude;
            history.Longitude = past.Longitude;
            history.EditDate = DateTime.Now;
            history.Add();
        }
    }
}

We’ve got all our basics: Get all, get one, create, delete. There are also methods for adding and saving. It seems SubSonic uses .Save() as either add or update, while .Add() and .Update() only work as, well, add or update. Also of note in the Update method is the call to SaveHistory. This is meant to show an update history of the messages, though I am not currently sure if I am going to enable editing. Better to have this in now and not need it, I guess.

Finally, we have the GuestbookController which uses the dependency injection I just set up. Here is the code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Ajax;
 
using MvcApplication.Models;
using MvcApplication.Repositories;
 
namespace MvcApplication.Controllers {
 
    public class GuestbookController : Controller {
 
        private IGuestbookRepository gr;
 
        public GuestbookController(IGuestbookRepository repository) {
            gr = repository;
        }
 
        public ActionResult Index(int? page) {
            const int pageSize = 5;
            var guestbook = gr.GetAllMessages();
            var pagedGuestbook = new PaginatedList<mvc_guestbook>(guestbook,
                page ?? 0,
                pageSize);
            return View(pagedGuestbook);
        }
 
        public ActionResult AjaxIndex(int? page) {
            const int pageSize = 5;
            var guestbook = gr.GetAllMessages();
            var pagedGuestbook = new PaginatedList<mvc_guestbook>(guestbook,
                page ?? 0,
                pageSize);
            return PartialView("Messages", pagedGuestbook);
        }
 
        [AcceptVerbs(HttpVerbs.Get)]
        public ActionResult Create() {
            if (Session[Request.ServerVariables["REMOTE_ADDR"].ToString()] != null ? DateTime.Now.Subtract(Convert.ToDateTime(Session[Request.ServerVariables["REMOTE_ADDR"].ToString()])).Minutes >= 5 : true) {
                mvc_Guestbook guestbook = new mvc_Guestbook();
                return View(guestbook);
            } else {
                Session["DELAY"] = (5 + Convert.ToDateTime(Session[Request.ServerVariables["REMOTE_ADDR"].ToString()]).Subtract(DateTime.Now).Minutes).ToString();
                return View("Denied");
            }
        }
 
        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult Create(mvc_Guestbook guestbook) {
            if (ModelState.IsValid) {
                try {
                    guestbook.MessageDate = DateTime.Now;
                    gr.Save(guestbook);
                    Session[Request.ServerVariables["REMOTE_ADDR"].ToString()] = DateTime.Now.ToString();
                    return RedirectToAction("Details", new { id = guestbook.GuestbookID });
                } catch {
                    ModelState.AddModelError("Error", "Error saving message");
                }
            }
            return View();
        }
 
        [Authorize]
        [AcceptVerbs(HttpVerbs.Get)]
        public ActionResult Edit(int id) {
            mvc_Guestbook guestbook = gr.GetMessage(id);
            if (guestbook == null) {
                return View("Not Found");
            } else {
                return View(guestbook);
            }
        }
 
        [Authorize]
        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult Edit(int id, FormCollection collection) {
            mvc_Guestbook guestbook = gr.GetMessage(id);
            try {
                UpdateModel(guestbook);
                gr.Update(guestbook);
                return RedirectToAction("Details", new { id = guestbook.GuestbookID });
            } catch {
                ModelState.AddModelError("Error", "Error saving message");
                return View(guestbook);
            }
        }
 
        [Authorize]
        [AcceptVerbs(HttpVerbs.Get)]
        public ActionResult Delete(int id) {
            mvc_Guestbook guestbook = gr.GetMessage(id);
            if (guestbook == null) {
                return View("Not Found");
            } else {
                return View(guestbook);
            }
        }
 
        [Authorize]
        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult Delete(int id, string confirmButton) {
            mvc_Guestbook guestbook = gr.GetMessage(id);
            if (guestbook == null) {
                return View("Not Found");
            } else {
                gr.Delete(guestbook);
                return View("Deleted");
            }
        }
 
        public ActionResult Details(int id) {
            mvc_Guestbook guestbook = gr.GetMessage(id);
            if (guestbook == null) {
                return View("NotFound");
            } else {
                return View(guestbook);
            }
        }
    }
}

For the code to wire this all up, I used Shiju’s example of the HttpContextLifetimeManager class as well as the UnityControllerFactory class. However, since I wasn’t using the NerdDinner code but my own Guestbook, I had to make the following changes to the UnityControllerFactory.Configure method:

1
2
3
4
5
6
7
8
9
10
11
public static void Configure() {
    //create new instance of Unity Container
    IUnityContainer container = new UnityContainer();
    //Register dependencies
    container.RegisterType<IFormsAuthentication, FormsAuthenticationService>();
    container.RegisterType<IMembershipService, AccountMembershipService>();
    container.RegisterInstance<MembershipProvider>(Membership.Provider);
 
    container.RegisterType<IGuestbookRepository, GuestbookRepository>(new HttpContextLifetimeManager<IGuestbookRepository>());
    ControllerBuilder.Current.SetControllerFactory(new UnityControllerFactory(container));
}

Finally, as Shiju illustrates, I added the call to UnityControllerFactory.Configure() into the Application_Start() method in my Globals.asax file and all is well.

All in all, I wouldn’t say I am 100% comfortable with exactly all that is happening in the code, but it is a good start at understanding DI with .NET MVC.

Manipulating an unordered list of links using jQuery

WARNING: This code was copied/pasted in from my old blog and there was some problem with the code example. It currently won’t work

The online fantasy football hosting site I use has lots of capability to modify layout and CSS, but some things are hard-coded and are only able to be manipulated using the DOM. They offer capabilities similar to that of MySpace. By that I mean you can add CSS and scripting via “Home Page Messages” so you can enhance and manipulate the layout of your site.

Someone asked how to manipulate navigation using javascript. I thought I’d tackle the task and get some more jQuery practice while I was at it. Here is what I came up with:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
$(document).ready(function() {
    removeMenuLink($hm, HM_MENU, "Message Board");
    addMenuLink($hm, HM_MENU, "Test", "#/Test/Link", 1);
    addMenuLink($vm, VM_LEAGUE, "Example", "http://www.example.com", -1);
    removeMenuLink($vm, VM_LEAGUE, "Accounting");
});
 
var $hm = "#hsubmenu ul";
var $vm = "#vsubmenu ul li ul";
var HM_MENU = 0;
var VM_FRANCHISE = 0;
var VM_LEAGUE = 1;
var VM_STANDINGS = 2;
var VM_PLAYERS = 3;
var VM_HELP = 4;
 
function addMenuLink(menu, section, name, url, idx) {
    var $ul = $(menu);
    var link = "" + name + "";
    if (idx > -1 && idx < $ul.find("li").length) {
        $($($ul[section]).find("li")[idx - 1]).before(link);
    } else {
        $($ul[section]).append(link);
    }
}
 
function removeMenuLink(menu, section, name) {
    var $ul = $(menu);
    $($ul[section]).find("li").each(function() {
        var txt = $(this).find("a").text();
        if (name == txt) {
            $(this).remove();
        }
    });
}

As you can see, there are two functions: addMenuLink and removeMenuLink. There were requests for both, so I took a shot at each. Since many of the users have no clue how to use javascript and/or CSS, I tried to make the example as simple as possible. I created variables to hold the jQuery selectors for each menu (the site users asked to modify a horizontal and/or vertical sub-menu on the site). I also defined “constants” for that define the index of sections of the menus: the horizontal menu is a single section, and the vertical is broken up in to 5 sections. I had to define the single horizontal section to enable code reuse.

Each of the functions is called with a reference to the menu and the section. The addMenuLink function also takes in the link text, URL, and the list position to place the new link. The removeMenuLink simply takes an additional string of the menu link text to remove. I wanted to make it more simple than asking for an index of the item to remove. As I said, these users don’t necessarily know what they are doing beyond the brief instructions and help given to them on the site forums.

Anyway, I don’t know how useful this is, but it may come in handy for someone out there on the tubes. Enjoy!

Flash Pageflip Code

I am cleaning out my old code snippets to see what I can post on the new blog. Here is a sweet little Flash pageflip app based off some source from PageFlip v2.13 coded by Macc@iparigrafika.hu.

About the code

If you did not already know, I work for an educational publisher. We have an online subscription-based site which is essentially most of our titles chopped up and indexed into small teaching units. Users pay a fee to have unlimited print capability. When we started the site, we were using PDF’s with an encryption plug-in. We want to allow our users the print capability (it is the backbone of the site) but we did not want them to store the files. We wanted them to use the site to “store” them, and we also did not want them distributing PDF’s. The plug-in works, but there are issues.

First off, the Mac version is a bit behind the Windows version. Since we deal with teachers and schools, we must have a Mac solution. The plug-in also only works with Adobe Reader. Since Mac come with Preview, users got confused about why they could see other PDF’s but not ours. Since Preview couldn’t use the plug-in, the users basically saw a bunch of blank pages since the PDF contents were never decrypted. Lastly, we used a Java based virtual page-flip viewer so users could see the contents of the file before downloading the PDF and printing. The major headache is the users had 3 pieces of software to install the get the full experience. Not good.

Alternative solution

Since we were having so many issues with installations and third-party software (reader wouldn’t recognize the plug-in, but the manufacturer’s installer said it was installed, Reader version was too old, Reader’s browser plug-in stinks, etc.) Istarted to look to alternatives. We considered writing our own PDF viewer, but quickly scrapped that idea. Then I started to look at Flash Paper. This looked like a pretty good alternative, but since the Macromedia/Adobe merger, the product is now treated like an illegitimate child.

I found Scribd, a start-up based in SF,CA. They took Flash Paper and basically rewrote it and extended it. It is really a pretty sweet thing. There were a couple issues, however. First, they would not let us purchase the code or host content on our servers. We have over 10,000 units, and we weren’t really eager to let them go, let alone the time it would’ve taken to upload (my company had a T1 back then and it was slooooow). We tried to come to an agreement but they just wouldn’t budge. In retrospect, I think it had to do with there PDF to SWF system (based on SWF Tools) and the fact that the “viewer” is really pretty basic. I think if people could run this locally they’d see that the system isn’t really that complex. The service is free, however, so it’s not like we wouldn’t get our money’s worth, right? However, all that aside, we still had issues with some of our PDF’s and really slow load times. We couldn’t have that, so I had to look elsewhere.

Pageflip

At this time the Flash pageflip stuff was pretty new, and quite cool IMHO. I thought it’d be pretty slick to use something like that and make my own Flash viewer (to replace the Java solution) and also use the Flash printing capabilities. This would solve all our problems! So, I downloaded this code and got to work. The code was using static page references, so I got it working with the XML file to dynamically load images into the viewer. It has been a while, so I don’t recall where the original code ended and mine began. Beyond that, I am no Actionscript guru, so I just kinda futzed around to get what I needed.

In the end, however, this was not the solution we used. It turns out to get that fancy flip action requires multiple versions of each page (3 if I recall correctly) and I could not figure out how to optimize it. Other than that, Flash basically just sucks. There is no garbage collection that can be called. Flash just decides when it will GC. Also, Flash has the nice undocumented feature of consuming every last drop of resources from your PC. We deal with user’s that have older PC’s, and our tests could actually crash browsers and lock-up machines. Thanks, Adobe!

What you get

Included in the zip are 2 versions of the viewer. the 2nd has an improved zoom capability. I put them both in so you could choose which one you wanted, and to let you see the differences I implemented regarding the zoom.

If you keep the number of pages small, or even keep them pretty low res, this will actually work okay. Did I mention you can also print? Oh, well, yeah, you can also print. All in all it is pretty groovy, but it just did not work out.

    Features:

  • Zoom capability
  • Print entire “document”
  • Dynamic page loading
  • Ability to add custom logo to viewer

Sample 1 Sample 2

Download
Preview

I hope someone out there finds a use for this. Enjoy!