in

Telligenti

Serving up fresh ideas every day, Telligent style

This Blog

Syndication

Dan Hounshell

  • Looking at ASP.NET MVC – following my own lead

    Several months ago I posted about one of my newest sites, ASPDotNetMVC.com. One of the main reasons for creating the site was for my own selfish purposes. I had begun digging into ASP.NET MVC and I wanted a site that aggregated all the good ASP.NET MVC news and blog posts so I could keep up to date. Since that time I’ve done a lot of reading but little doing, other than a side trip with Kigg, a Digg clone written with ASP.NET MVC (Upgrading Kigg to ASP.NET MVC Preview 3, How to decouple the ASP.NET Membership database from Kigg and How to use Kigg as a service from another site).

    This weekend I finally made some time to get my hands dirty. My first task is to convert the ASPDotNetMVC.com site from web forms to ASP.NET MVC. So far things are going well and I’m actually surprised at how quickly it is progressing. In a couple of hours I was able to get the master page setup and convert all of the static pages and content to controllers/views (things like the About page, some of the home page, etc). The next step, which I’m currently in the middle of, is converting the Contact Us form. Then the more difficult parts: converting existing controls, rss feeds, and the dynamic content (the majority of the site!). It’s been a long time since I’ve had to hit Google for nearly every little piece of code that I write. It’s a bit frustrating, but challenging and invigorating at the same time.

    I won’t set a timeline because I don’t know what challenges I will run into yet. I should have time through the holidays to keep plugging away. I’ll be sure to post if I run into anything interesting.

    BTW, I updated my Preview 3 version of Kigg to the ASP.NET MVC Beta bits on Friday night and it was a piece of cake. I just followed the Beta Release Notes instructions for “Upgrading and Existing Preview 3 application to Beta” and things went well.

  • Can I get a Mulligan on the case order?

    This is part 4 in a series about building an Eco-Friendly Economical Windows Home Server.
    Part 1: In search of an Eco-Friendly Economical Home Server
    Part 2: In search of an Eco-Friendly Economical Home Server 2, Electric Boogaloo
    Part 3: A Cheap and Green Windows Home Server
    Part 4: Ordered parts for Eco-friendly Economical Windows Home Server

    After some post-mortem review of my order I’m feeling some buyer’s remorse. The case that I ordered for my eco-friendly economical Windows Home Server box wasn’t as small as I remembered to be. At 15.4” x 7.1” x 14.8” (HxWxD) it is a little smaller than the case of my Dell Dimension 5150 but it is nowhere near the size of the HP MediaSmart servers at 9.8” x 9.2” x 5.5”. But it was still cheap at $39.99 and free shipping!

    If I were looking for something a little more compact and a little more stylish while still fitting into my budget theme (and if I had a Mulligan) I would probably opt for something more like one of these Athenatech cases:

     athenatech_a100sc200Athenatech A100SC.200 Black/ Silver Steel MicroATX Desktop Computer Case 230W SFX Power Supply – Retail 
    13.5” x 5.8” x 14.5" (HxWxD)

    $42.99
    Shipping: $11.99

    athenatech_A100bb270Athenatech Black Steel A100BB.270 Micro ATX Media Center / HTPC Case – Retail
    13.5” x 5.8” x 14.5" (HxWxD) $49.99
    Shipping: $11.99athenatech_A106SB

    Athenatech Silver/ Black Steel A106SB.200 Micro ATX Media Center / HTPC Case – Retail
    14.5” x 5.8” x 15" (HxWxD)

    $62.99
    Shipping: $15.99 


    The A100SC and the A100BB are both 2 inches shorter, more than 1 inch narrower, and about the same depth as the CompuCase 6K28BS and I think they look a little sleeker, too. Swapping for either of these would increase the build price by $15-$20. The A106SB is not quite as diminutive as the other two but it still comes in 1” shorter and 1” narrower than the CompuCase 6K28BS, but looks a lot nicer for a $35 price increase. Any of these three cases would look just as good set horizontally or vertically. I like the looks and reviews of these cases, I may keep them in mind for a future budget Media Center/HTPC build. Or I might just go ahead and order one of these now for my WHS box and store the previously ordered case for a future project.

  • Ordered parts for Eco-friendly Economical Windows Home Server

    This is part 4 in a series about building an Eco-Friendly Economical Windows Home Server.
    Part 1: In search of an Eco-Friendly Economical Home Server
    Part 2: In search of an Eco-Friendly Economical Home Server 2, Electric Boogaloo
    Part 3: A Cheap and Green Windows Home Server

    What a difference six months makes in the price of run-of-the-mill computer desktop hardware. In my last article the cheapest I could get a decently powerful Home Server setup was for about $470 including shipping. By the time I added the $149 cost for Windows Home Server I was in the same ballpark as the HP MediaSmart Home Server setups, though it was a little greener and a little more powerful. Still, it didn’t make a lot of sense to go ahead and do the build.

    Fast forward 8 months and with a little tweaking of chosen parts I was able to order a kit with better specs than the $650 version of the HP Home server for just over $300. That price does not include the cost of WHS itself ($149), but I’ll be enjoying a 120-day eval for a couple of months so I can defer that cost. Even if I included it, though, I’d still be at only $450, which is about $200 less than the similarly spec’d (yet still not as much RAM or CPU and not as green as my setup) HP MediaSmart EX 475 at around $650. At $200 less and better specs the timing is right for me to pull the trigger and do it. 

    Here are the parts I ordered a few nights ago from NewEgg. I should receive everything this week so I plan on putting it together next weekend and then basking in the wonderfulness that is promised with WHS.

     hec6k28bsHEC 6K28BS Black / Silver 0.8mm SECC Steel MicroATX Mini Tower Computer Case - Retail

    $39.99
    Free Shipping

     antec_earthwatts_380Antec earthwatts EA380 380W ATX12V v2.0 80 PLUS Certified Active PFC Power Supply - Retail $29.99  WDGP500GB

    Western Digital Caviar GP WD5000AACS 500GB 5400 to 7200 RPM 16MB Cache SATA 3.0Gb/s Hard Drive – OEM x 2

    $64.99 x 2
    Free Shipping
    Kingston1GBDDR2800Kingston 1GB 240-Pin DDR2 SDRAM DDR2 800 (PC2 6400) Desktop Memory - Retail $15.49  AMDAthlonX2BE2400AMD Athlon X2 BE-2400 Brisbane 2.3GHz 2 x 512KB L2 Cache Socket AM2 Dual-Core Processor - OEM $39.99
    Free Shipping  MSIK9A2GM-FMSI K9A2GM-F V3 AM2 AMD 740G Micro ATX AMD Motherboard - Retail $52.99
    Open Box:
    $36.76  MASSCOOL5f9001b1h3MASSCOOL 5F9001B1H3 80mm Ball CPU Cooler - Retail $9.99

     

    • CASE: I wanted a case that was small(ish), looked pretty good, fairly quiet, and could hold 4 internal hard drives. The case I chose has a nice clean look, got good reviews on NewEgg, and it is fairly small: about the same size as the HP MediaSmart boxes. Over the next week I will re-review blog posts and articles I’ve read in the past about building a quiet PC.
    • POWER SUPPLY: I wanted an 80+ certified power supply in about the 300W range. I had been looking at the 300W Seasonic at about $40. But then I saw the Antec Earthwatts on sale for $30 and I couldn’t pass up the $10 in savings. Plus I’ve read in a couple of places that Seasonic makes those power supplies for Antec. That may or may not be true, but even if it isn’t true the two must be very close in quality for the comparison.
    • HARD DRIVES: I’m sold on the Western Digital Green Power drives for this application. I got two 500GB drives at $65 each. These things were over a hundred dollars each 8 months ago. With the price of the 1TB versions at just $129 I came very close to going with two of those instead. Personally I would have done that, but the WAF (Wife Acceptance Factor) played a big part in that specific decision.
    • RAM: The HP WHS boxes come with just 512MB of RAM, even the higher end box with 1TB of drives, so I figured that is a good place to start with my setup. I could have gone with 2GB of RAM here for $30, but I figure that 1GB is good enough for now and if the box needs more I can easily and cheaply add another 1GB stick later.
    • CPU: I think that the AMD Athlon X2 BE-2400 may be the perfect CPU for this setup. It is 2.3 GHz, dual core, 512KB L2 Cache AND it’s rated at 45W – the same as the AMD single core processers. Plus it was only $40! So basically for the same price and footprint as a single core 1.8 GHz Sempron (like what is in the HP boxes) I was able to go with faster dual cores and more L2 cache. That is a no-brainer!
    • MOTHERBOARD: The minimum specs that I wanted for a board were MicroATX, AM2 processor, 4 SATAII connectors, support for at least 4GB of RAM, onboard Gigabit Ethernet, and lots of USB connectors. I chose the cheapest name-brand board that met those requirements and had good reviews. I was also lucky(?) enough to find an open-box version for $15 less. The jury is still out on whether the open box version was a good choice – I’ll let you know the answer to that after I assemble and make sure it isn’t DOA.
    • CPU COOLER: Since I ordered an OEM CPU it doesn’t come with a cooler. I chose one similar to the OEM style that was cheap and got good reviews. It should meet my needs fine.
    • OTHER: The reviews I read about the HEC/Compucase case that I selected said that the rear case fan was sufficient and rather quiet (surprising for one that’s included in a case) so I’ll delay purchasing any additional or replacement case fans for now.  

     

    The total price for everything that I ordered was right about $310 (I added 2 SATA cables that I may or may not need) and was about $330 after shipping. Had I not added the SATA cables and gone with an AMD single-core Sempron (saving $10) the total price including shipping would have been right around $300.

    I’m very pleased with the parts and the value. Hopefully I’ll be just as pleased with the end result. I plan a follow up with notes on the build after next weekend.

    I’m excited. I cannot wait for the BBT (Big Brown Truck) to show up at the door with my box(es) of parts.

  • Sample OpenID ASP.NET Web Site

    A couple of months ago I posted how to add OpenID to your existing web site in conjunction with ASP.NET Membership. In the comments a few times I was asked to post the code to the OpenID login user control or to create a sample project to share. Even though there really isn't a lot to the code other than what I shared in that article I said that I would post the sample code. After ScottGu linked to my original post last week I felt compelled to step up the pace of doing so.

    Below is a link to the sample solution/project/web application. There is really nothing to the web site itself, just a static home page, a "join" page and a "login" page that use traditional membership controls and the OpenID login user control. Go ahead and check out the code and feel free to use that control and the accompanying utility code in your own projects. Please read the Readme.txt file included in the zip for setup instructions.

    Sample OpenID Web Site

     

  • Save your Kudos for your Annual Review

    Over the last several years I’ve gotten into the habit of saving any “kudos” emails that get sent to me into a folder with the creative name of “Kudos”. I don’t know where I picked up this habit, but from time to time I’ve shared this tip with others. After letting Jose in on my little secret earlier today he suggested that I blog about it and share it with everyone. Good idea, Jose. Hopefully this doesn’t kill my competitive advantage :), but here goes…

    When the occasional email comes in that says “Hey Dan, good work staying up late and making sure that foo got fixed. You really went above and beyond this time. You rock!”, I stash it away into my Kudos folder for safe keeping.

    No, I don’t keep the “atta boy” emails in one place for an easy pick-me-up when I’m feeling like a dummy – though they do help – that and a beer or two or three. I have a more pragmatic reason for keeping them – for easy access come annual review and/or self-evaluation time. Prior to a review I will thumb through them looking for some good examples and evidence that I am indeed a useful employee.

    Because of this evidence rather than just telling my reviewer “I’m a hard worker; I go the extra mile.” and hope he or she takes my word for it, I have proof to back up my claim in the form of a quote or two from those emails. I can then follow up my claim with “… and Willy, your boss, thinks so too – here’s an email I got from him in February where he said so.” Instead of just stopping at saying “I communicate well with others, both co-workers and clients” I can pull a couple of quotes from my stashed emails from a co-worker and a client to support it.

    Saving, organizing, and using “kudos” emails has proven to be a good tool for me come review time. Even if you’re not a traditional employee this can still be useful. As an independent contractor having a repository where you can pull “quotes from satisfied clients” might be valuable. Or as a micro ISV it will give you fodder for that “what people are saying about Product X” spot on your web site.

    Bonus Tip #1: A lot of written conversation these days take place in mediums other than email. Twitter, instant message clients, and LinkedIn are good examples of this. If you get an IM or other message that contains a special “way to go” message, copy and paste it into an email to yourself (along with who it was from, a date/time, and a note about the circumstances) and move it to your Kudos folder so it will be stored along with everything else.

    Bonus Tip #2: The Kudos folder is a good place to store other things besides emails and “atta boy” messages. If I get a good evaluation for a presentation I did at a .NET user group I’ll store the results there. If I write a blog post that shows up on the front page of DotNetKicks I’ll take a screenshot, make a note, and store that in Kudos. If I implement a widget for Client XYZ and they see an immediate 20% increase in traffic I’ll make a note and save that, too. I may not use each and every thing that I put in there for reviews, but if I think it might be useful at some time in the future for some shameful self-promotion I’ll save it. It’s reassuring to know that if I ever feel the need to find some long forgotten piece of information like that I can easily find it in that one folder.

    Good luck. Great Job. Kudos!

  • The double dog dare gets me every time!

    I wondered when some of these pictures from DevLink would surface. Between the dares and doubts of those who said I couldn't fit into a medium t-shirt - I had to do it. A big "thanks" to Dave Giard for capturing the moment. Check out the guns! I should have flexed for the pics!

    danh_geekette

     

  • My cast-offs may be your treasure

    As a web developer I like building new web sites, web applications, mashups, widgets, etc.

    Just like most other men I like shiny new toys and sometimes not-so-shiny older toys tend to become less used, become a little rusty and maybe even feel a little unloved.

    If I were able to find a way to graph those two statements we'd see that their intersection occurs at about 9-12 months and afterwards usage drops off dramatically. By that time I've moved on to the next shiny new toy - I've already started working on a new personal project. I like building sites but I don't necessarily like running them over the long haul.

    If you're not afflicted with this disease, are interested in leveraging social networking sites or just looking for a good domain name investment or an SEO investment then I have a deal for you. In an effort to simplify my portfolio I am selling the following domains/site(s):

    RandomTweets.com (Google Page Rank = 4)
    RandomTwitter.com - redirects to RandomTweets.com
    RandomTwits.com - redirects to RandomTweets.com

    RandomJaikus.com (Google Page Rank = 4)
    RandomJaiku.com - redirects to RandomJaikus.com

    RandomPownce.com (Google Page Rank = 4)

    RandomFlickr.com (Google Page Rank = 3)

    The domains, the sites, all source code, etc. will be included - you will have to find your own hosting. I do not plan on splitting them up unless someone makes me an offer I cannot refuse, but I will consider offers for just the domain names themselves (all of them together). Offers for the entire kit will be considered before offers for just the domain names.

    For more information about the sites, the code, the domains for sale, traffic history, etc. or to make an offer contact me through my site or email me at: sites4sale AT danhounshell.com.

  • Telligent Wants You!

    I joined Telligent nearly two years ago. Over that time we've grown nearly threefold and we're still growing and going strong.

    Scott W, Rob Howard, Dave Donaldson, the Pentonizer and others have already said it but it bears repeating... We need more great developers! We want to do some really great things in the next version of Community Server, but we need more people to help make them happen. Check out Scott's post for the skills we are looking for. If you fit the bill then we want to hear from you.

    Do you want to become a member of the Telligent team? Give me a shout at dhounshell@telligent.com.

  • One Thought: Why Regional Conferences + Open Spaces Rock

    I'm still trying to gather my thoughts after attending DevLink this past weekend. One thing that I want to highlight is how great these regional conferences are and how much they are a perfect fit for Open Spaces. [Much props to Alan Stevens for making the DevLink Open Spaces a huge success]

    One thought defines this for me:

    How much would you pay to take part in a group discussion with leading-edgers of the .NET TDD movement like Alan Stevens and Steven Harman and ask them anything you want about what they're doing, how they're doing it, why they're doing it and even have them fire up a laptop and give you an example. What if they offered to do some pair programming with you on the spot to help you grok what they are talking about? Did it!

    And if you're not interested in TDD then what if the topic was something else you are interested in? Ruby - got you covered by Ruby heavyweights  [No pun intended - much love!] like Corey Haines, FallenRogue, and Joe O'Brien. Did it. Want to ask questions about SOA from people who are neck deep into it every day  like James Bender and Michael Neel? Covered there, too. Is there anything that you are passionate about that you'd like to talk with others equally as passionate? That is the beauty of Open Spaces - you can and will.

    Wait, so what is the cost? Because it's a regional conference it is affordable. How affordable? How does less than $100 for two days sound? How about even $50 for two days? Truly a bargain in my book.

    We WILL be carrying on what we've learned about Open Spaces over the last year in the Heartland District to CodeMash in January in Sandusky, OH. I hope to see you there.

  • Community Server does "Am I ___ or Not?"

    CommunityServer_AmIHotOrNot

    We're always working on something cool at Telligent, whether it be an entirely new product or some type of addition or extension to Community Server. Some good examples are some of the things we came up with in Hackathon II, including the couple of IM clients that work with Community Server messaging. If you haven't already read about some of the thing we came up with you can see them here and here.

    Recently over a late night IM session with Dave Donaldson, we pondered the benefits of certain types of social bookmarking functionality as well as other Web 2.0-like features. After our talk my thoughts turned to even more features and functionality and eventually settled on "Am I Hot or Not?". No, I'm not a perv - well not most of the time. However, a long time ago I built a clone called "Am I Dan Hounshell or Not?" in about an evening and ran it as part of my site for a while. My friends and I had a lot of fun with it.

    I began wondering what it would take to to build the same type of application on top of Community Server. CS has a media gallery, right? It shouldn't be too difficult to build the rest of the needed functionality on top of the media gallery, right? As I began looking into it I realized that my initial assumptions were wrong. Everything for Am I ___ or Not? is already there. In fact if you look at the screen shot at right you'll see a mini Am I Hot or Not application running right inside of Community Server! That's with a bone stock installation of Community Server 2008 running on my local PC right now, with no modifications, no skin changes, nothing. Everything is built in :). [Editor's Note: Maybe in the case of the jokers in the screenshot it should be "Am I Nerdy or Not?"]

    Community Server already has a Media Gallery which allows for uploading and viewing images, among other things. But it also has ratings and comments for each image/file built-in as well. You can also sort by Most Recent, Most Downloads, Most Popular and Most Comments. You can view all the images as a slideshow and there is even RSS! What more do you need for an Am I ___ or Not site? The only thing I needed to do was create a new Media Gallery, named "Am I Hot or Not?" (of course) and then give Registered Users permission to upload files to that gallery. If you want you could even add a link to the gallery in your site's main navigation.

    I doubt that most people and organizations running Community Server need or even want an Am I ___ or Not? application as part of their site (or give all registered users the ability to upload files to a gallery), but for the one or two of you out there who do - look no further than the Community Server platform you already have!

    And if you're wondering what else you can do with Community Server, come join us at in.telligent 2008 in Dallas October 20-22 and take part in some of the great workshops, including theming and extending Community Server.

  • Lan and Deon #3

    Lan and Deon go to their local .NET User Group meeting...

    Those who live in glass houses...

     

  • I like VisualSVN

    VisualSVN I'm not a big fan of Source Code Management integration with Visual Studio. I don't know, maybe VSS soured me years ago. When using Vault in the past I installed the Visual Studio integration tools once and uninstalled them a few days later. While using Subversion almost exclusively over the last three years I've been more than happy using TortoiseSVN in an explorer window. The separation of SCM and programming IDE has been refreshing and actually lends itself well to the way Subversion works by default, not requiring a checkout or lock when editing a file.

    A few months ago, however, I won a license for VisualSVN at the Indy Code Camp. The license sat on a table beside my desk for about a month before I finally decided to give it a spin. I have to admit that I'm growing fond of it. I still don't run updates and commits inside VS using VisualSVN (even though it just defaults to TortoiseSVN), but I really like the visual indicators that it provides. Showing me which files I've added or modified with the yellow icon greatly reduces any chance of me forgetting files when I make a commit. Or it helps me quickly find any files that I may have forgotten to commit.

    The other cool thing is that when I add any new files to a project via Visual Studio, VisualSVN automatically adds the file to SVN. I don't have to remember to browse to the file in Windows Explorer, right click the file, choose TortoiseSVN and then click Add. That may not seem like much of a time saver, but I always forget to manually add the files until after I've done commit (and maybe broken the build).

    Even it you don't like mixing your chocolate and peanut butter, um... I mean SCM tools with your IDE, you may like some of the features that VisualSVN provides. Give it a try, they offer a 30-day trial and the licenses start at $49 per seat.

    Okay, that probably came off sounding like a commercial for VisualSVN. I am in no way affilated with the product or company and I stand to make no money from a couple more licenses being sold. I simply like the product and I thought I'd pass on my experiences with it. Besides, "if I can change and you can change, we all can change!" :)

  • Adding OpenID to your web site in conjunction with ASP.NET Membership

    AspDotNetMVC_OpenIdLoginI recently added membership, accounts, login, etc. to the AspDotNetMVC site. While doing so I decided I wanted to support OpenID, too. However, I didn't want to go with only OpenID because I needed ASP.NET Membership in place to work in conjunction with another application, a Kigg site used as a service for rating content on the AspDotNetMVC site. I could have probably converted the Kigg code base to use OpenID but I also wanted to allow people who may not have OpenID to create traditional accounts on the site without signing up for OpenID. Following are the steps I took to implement an OpenID login and integrate it with "traditional ASP.NET" membership. Follow these six steps and you can do the same.

    1. Download the awesome C# OpenID library, DotNetOpenId from Google Code, and add it as a reference in your project. This is a great open source library developed by Andrew Arnott, Jason Alexander, Scott Watermasysk, Scott Hanselman, Joe Brinkman and others. It seriously does all the heavy lifting, comes with some good examples, and Andrew Arnott is doing a great job of making posts about fringe cases on his blog.

    2. Add some nice usability features to your OpenID login. Go to ID Selector, create an account and grab the couple of lines of JavaScript to create the cool OpenID service selector shown in the image at right.

    Did I mention that this is so easy that I don't know why everyone isn't supporting OpenID? Going forward I will be doing so in every new site that I create and I'll retrofit existing sites when I get the chance.AspDotNetMVC_OpenIdLogin2

    3. Read the following articles:

    After reading each of the above you will have a pretty good understanding of how ot add OpenID to your site. Some of the code examples in the earlier 3 posts by Andrew are a little out of date. As of the writing of this post the code provided in Hanselman's blog post are the most up to date. Go ahead and read the two "MVC" posts by Andrew even if you are not interested in ASP.NET MVC. The concepts displayed in those posts can be used anywhere. In fact, it was in of those posts where I found my inspiration for overcoming my next hurdle.

    The only thing that was missing for me was integration with ASP.NET membership. I actually found very little information for accomplishing this. I found quite a lot of questions asking how to do it, but most of the answers were just more questions asking why you would want to do it.

    4. Create your login form (or user control or composite server control or whatever you prefer). In my case I created a user control that holds just the fields for OpenID login to my user control. Doing so allows me to drop that control into my existing login page (shown in image above) or my existing create account page or anywhere else I want to use it. Make sure you add the javascript for the ID Selector.

    For allowing traditional ASP.NET membership login and user account creation I just dropped the basic out of the box controls on the page.

    5. Wire up the login submit button. I did something like below, which is very similar to examples that Andrew and Scott Hanselman provided. Basically I'm just telling the OpenID provider that I need the user's email and nickname (for use in the next step):

    protected void loginButton_Click(object sender, EventArgs e) { if (!openidValidator.IsValid) return; // don't login if custom validation failed. OpenIdRelyingParty openid = new OpenIdRelyingParty(); try { IAuthenticationRequest request = openid.CreateRequest(openid_identifier.Text); ClaimsRequest fetch = new ClaimsRequest(); fetch.Nickname = DemandLevel.Require; fetch.Email = DemandLevel.Require; request.AddExtension(fetch); request.RedirectToProvider(); } catch (OpenIdException ex) { // The user probably entered an Identifier that // was not a valid OpenID endpoint. openidValidator.Text = ex.Message; openidValidator.IsValid = false; }}

    6. Handle the response from the OpenID provider. This snippet of code is part of the login process and expands upon examples from Andrew Arnott's and Scott Hanselman's posts linked to previously.

    Here I'm pulling the alias and email address that I requested in the previous step. With that information I check to see if the user already exists in the ASP.NET membership datastore. If not I will create a Membership account for them using their OpenID URI as their username. When I create the account you can see that I'm generating a random string for the user's password field and password answer field. You'll also see that I'm adding "This is an OpenID account. You should log in with your OpenID." as the password question. That way if a user forgets they used OpenID and tries to login through the traditional username/password login form and selects that they forgot their password when the login doesn't work, he or she will get a reminder about their OpenID in the form of the password question. I know it's a hack, but it works for now for my modest needs and I'm happy with it.

    Next you'll see that after I create the Membership User I then set the user's "comment" field to their Nickname. This is because the "Hello, Username. Welcome back to the site." message at the top of the page would normally display the user's "username". In the case of OpenID users their username would be their OpenID URI - something like: http://danhounshell.openidprovidername.com/. That's a bit ugly and I'd rather use the Nickname that I asked from then when they logged in with OpenID. By shoving the Nickname into the comments field I can check to see if that is filled in first and use it in the display rather than their username and if it is not then default to the username. So now using my previous example it will say "Hello, DanHounshell. Welcome back to the site." This same thing could be accomplished in a couple of other ways. Throwing the nickname into the session object or putting in a cookie are other reasonable alternatives. In my case I don't use the comments field for anything else so once again - I know it's a hack, but it works for now for my modest needs and I'm happy with it. Plus it's easy to get to my just writing "user.Comment". Thinking about it, it might be nice to write an extension method for MembershipUser called DisplayName that determines whether to use the user.Comment property or the user.Username property.

    Finally, I just call FormsAuthentication.RedirectFromLoginPage() passing in their OpenID URI that they provided, logging them into the site.

    case AuthenticationStatus.Authenticated: ClaimsResponse fetch = openid.Response.GetExtension(typeof(ClaimsResponse)) as ClaimsResponse; string alias = fetch.Nickname; string email = fetch.Email;   if (string.IsNullOrEmpty(alias)) alias = openid.Response.ClaimedIdentifier; if (string.IsNullOrEmpty(email)) email = openid.Response.ClaimedIdentifier;  //Now see if the user already exists, if not create them if (Membership.GetUser(openid.Response.ClaimedIdentifier) == null) { MembershipCreateStatus membershipCreateStatus; MembershipUser user = Membership.CreateUser(openid.Response.ClaimedIdentifier, Common.GetRandomString(5,7), email, "This is an OpenID account. You should log in with your OpenID.", Common.GetRandomString(5,7), true, out membershipCreateStatus); if (membershipCreateStatus != MembershipCreateStatus.Success) { loginFailedLabel.Text += ": Unsuccessful creation of Account: " + membershipCreateStatus.ToString(); loginFailedLabel.Visible = true; break; } user.Comment = alias; Membership.UpdateUser(user); } // Use FormsAuthentication to tell ASP.NET that the user is now logged in, // with the OpenID Claimed Identifier as their username. FormsAuthentication.RedirectFromLoginPage(openid.Response.ClaimedIdentifier, chkRememberMe.Checked); break;

    That's all there is to it. Now you can allow your user's to choose whether they'd like to create an account on your site by creating a new username and password or by using their new or existing OpenID. The bonus with this method is that it allows you to add OpenID support to an existing site that already has traditional membership without breaking anything.

    Enjoy and let me know if you have any questions or comments.

  • How to use Kigg as a service from other sites

    AspDotNetMVCGetsKigg

    In yesterday's post I started providing some detail about my use of Kigg to add rating functionality to the AspDotNetMVC site. Because I wanted to integrate the ASP.NET Membership store between the two sites into an external database I started by decoupling the membership data from the Kigg database, model and code.

    My end goal was to allow visitors to the AspDotNetMVC site to view the number of existing votes for each article (blog post, buzz, news, or video) and to vote for an article if they were logged in without having to jump over to the Kigg site to do so. Because each of the items listed on the AspDotNetMVC are basically just pointers to some content on the Internet at a unique URL I needed a way to request the rating count from Kigg by URL. I also needed a way to vote for an article by passing some (url, title, description, category, tags and user) from the main site to Kigg. Of course as a web developer my first thought was to create a web service in Kigg with two methods, GetVoteCount(url) and SubmitOrVote(url, title, desc...). Since I had never previously created a WCF web service I chose to go that route rather than the asmx approach.

    YMMV, but the first thing I needed was a new method in my Kigg data model to allow for retrieving the "story details" by url rather than id. In Kigg, like Digg, a story is an item submitted to the site. Since I'd be using an external site I wouldn't know the ID of a story (or care what the id was) I wanted to be able to perform lookups by the story's URL. I added the following to the KiggDataContext model (and the signature to the IDataContext interface):

    public StoryDetailItem GetStoryDetailByUrl(Guid userId, string url) { return Stories     .Where(s => s.Url.ToLower() == url.ToLower()) .Select(s =>     new StoryDetailItem {     ID = s.ID, Title = s.Title, Description = s.Description, Url = s.Url, Category = s.Category.Name, Tags = s.StoryTags.Select(st => st.Tag.Name).ToArray(), PostedBy = new UserItem(s.PostedBy), PostedOn = s.PostedOn, PublishedOn = s.PublishedOn, VoteCount = s.Votes.Count(), HasVoted = (s.Votes.Count(v => v.UserID == userId) > 0), VotedBy = s.Votes     .OrderBy(v => v.Timestamp) .Select(v => new UserItem(v.UserID)) .ToArray(), Comments = s.Comments .OrderBy(c => c.PostedOn) .Select(c =>     new CommentItem {     PostedBy = new UserItem(c.PostedBy), PostedOn = c.PostedOn, Content = c.Content }     ).ToArray() } ) .FirstOrDefault(); }
     

    This is very much just a copy and paste of the existing GetStoryDetailById method, just querying by URL rather than ID.

    Next I needed to create the web service and two new methods in it. I created the service at /services/KiggService.svc and the two methods look like the following:

    [WebGet(ResponseFormat = WebMessageFormat.Xml)][OperationContract]public string GetVoteCount(string url) { //MembershipID doesn't matter because we're just getting the vote count. Guid _currentUserID = new Guid(); IDataContext db = new KiggDataContext(); var story = db.GetStoryDetailByUrl(_currentUserID, url); if (story != null) return story.VoteCount.ToString(); return "0";}
    [WebGet(ResponseFormat = WebMessageFormat.Xml)][OperationContract]public string SubmitOrVote(string userid, string url, string title, string desc, string categoryName, string tag){ if (string.IsNullOrEmpty(userid)) return("You must be authenticated prior hitting this url"); Guid _currentUserID = new Guid(userid); Category[] _categories; IDataContext db = new KiggDataContext(); _categories = db.GetCategories(); try { Category cat = _categories.First(c => c.Name == "General"); if (!string.IsNullOrEmpty(categoryName)) { cat = _categories.First(c => c.Name.ToLower() == categoryName.ToLower()); } var categoryId = cat.ID; var story = db.GetStoryDetailByUrl(_currentUserID, url); if (story == null) { db.SubmitStory(url, title, categoryId, desc, tag, _currentUserID); return "1"; } else { db.KiggStory(story.ID, _currentUserID, 3); var story2 = db.GetStoryDetailById(_currentUserID, story.ID); return story2.VoteCount.ToString(); } } catch (Exception e) { return "error" + e.Message; } }

    There's not much to the GetVoteCount method, it just checks to see if a story already exists in Kigg for the given URL, if it does then it returns the vote count, otherwise it returns 0.

    The second method SubmitOrVote probably needs a little more explanation. It allows for a user to click a vote button on the AspDotNetMVC site and if the story already exists then it adds an additional vote. If the story does not already exist in Kigg then it submits it. Sounds simple enough. Since the category the story belongs in is passed in by a string I need to check the categories that exist in Kigg to find a match for that passed in categoryName string. First I set "cat" to the default category, "General", and then try to find the match. If there is a match then I use the Kigg category that I found, otherwise I stick with General. Next I just lookup the story and then give it the additional vote or create it if it doesn't exist and then return the appropriate new vote count.

    You'll notice that I'm passing the userId across the wire. In my case it's not a big deal because my calls to the service are done server side in my client application and the Kigg site is not publicly accessible. Since the Kigg site is only accessible by the AspDotNetMVC site I also don't have to worry about authentication or authorization either. Again YMMV. Also you may not want to expose your exception like I have to callers of your Kigg service. In my case it's fine because I'm the only one calling it and I wanted to be able to log any exceptions in the client application for later review.

    You'll have to add appropriate error handling around the calls to these web service service and it would be a good idea to cache the votes for each article/url/story in your consuming application so you aren't calling that method every time you need to show the votecount.

    That's all the adjustments that you need to make to Kigg to allow it act as a service for other applications. I'll admit that it did take me a while to get the web.config setup properly to allow for the WCF services, but that has more to do with my first time doing doing anything with WCF than it does with the Kigg updates. If you have any WCF experience at all I'm sure it will take you much less time than the 30 minutes I spent doing so (mostly spent searching for answers on Google).

    Hopefully these last two posts will provide you with a jumpstart for integrating Digg-style ratings (um... I mean Kigg-style) into your existing or new web applications. Have fun with those new mashups.