<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>Ryan's Blog</title>
	
	<link>http://www.ryanmwright.com</link>
	<description>The Geek Shall Inherit the Earth</description>
	<lastBuildDate>Tue, 12 Jun 2012 23:49:20 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.5.1</generator>
		<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/ryanmwright/bSlq" /><feedburner:info uri="ryanmwright/bslq" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><feedburner:emailServiceId>ryanmwright/bSlq</feedburner:emailServiceId><feedburner:feedburnerHostname>http://feedburner.google.com</feedburner:feedburnerHostname><item>
		<title>Codeplex Project for Dynamic Controller/Action Authorization in ASP.NET MVC</title>
		<link>http://feedproxy.google.com/~r/ryanmwright/bSlq/~3/4jyYRV9GD6w/</link>
		<comments>http://www.ryanmwright.com/2011/10/19/codeplex-project-for-dynamic-controlleraction-authorization-in-asp-net-mvc/#comments</comments>
		<pubDate>Thu, 20 Oct 2011 02:02:15 +0000</pubDate>
		<dc:creator>Ryan</dc:creator>
				<category><![CDATA[ASP.NET MVC]]></category>
		<category><![CDATA[linkedin]]></category>

		<guid isPermaLink="false">http://www.ryanmwright.com/?p=681</guid>
		<description><![CDATA[For those of you who used the code in my post here: http://www.ryanmwright.com/2010/04/25/dynamic-controlleraction-authorization-in-asp-net-mvc/ I have created a project on Codeplex for it (http://mvcauthorization.codeplex.com/). Note that this version has some major breaking changes from the code I posted in my previous &#8230;<p class="read-more"><a href="http://www.ryanmwright.com/2011/10/19/codeplex-project-for-dynamic-controlleraction-authorization-in-asp-net-mvc/">Read more &#187;</a></p>]]></description>
				<content:encoded><![CDATA[<p>For those of you who used the code in my post here: <a href="http://www.ryanmwright.com/2010/04/25/dynamic-controlleraction-authorization-in-asp-net-mvc/">http://www.ryanmwright.com/2010/04/25/dynamic-controlleraction-authorization-in-asp-net-mvc/</a> </p>
<p>I have created a project on Codeplex for it (<a href="http://mvcauthorization.codeplex.com/">http://mvcauthorization.codeplex.com/</a>). Note that this version has some major breaking changes from the code I posted in my previous post but has some major improvements, the two biggest being:</p>
<ul>
<li>Provider based model so that a database provider is as simple as overriding two methods on AuthorizationProvider</li>
<li>A global action filter for authorization (requires DependencyResolver)</li>
</ul>
<p>Check it out and let me know what you think. I will be adding a post to show how to integrate it with the MvcSitemap provider soon.</p>
<script type="text/javascript">(function() {var s = document.createElement('SCRIPT'), s1 = document.getElementsByTagName('SCRIPT')[0];s.type = 'text/javascript';s.async = true;s.src = 'http://widgets.digg.com/buttons.js';s1.parentNode.insertBefore(s, s1);})();</script><a class="DiggThisButton DiggCompact" href="http://digg.com/submit?url=http%3A%2F%2Fwww.ryanmwright.com%2F2011%2F10%2F19%2Fcodeplex-project-for-dynamic-controlleraction-authorization-in-asp-net-mvc%2F"></a><img src="http://feeds.feedburner.com/~r/ryanmwright/bSlq/~4/4jyYRV9GD6w" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.ryanmwright.com/2011/10/19/codeplex-project-for-dynamic-controlleraction-authorization-in-asp-net-mvc/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://www.ryanmwright.com/2011/10/19/codeplex-project-for-dynamic-controlleraction-authorization-in-asp-net-mvc/</feedburner:origLink></item>
		<item>
		<title>Active Federation with Windows Identity Foundation</title>
		<link>http://feedproxy.google.com/~r/ryanmwright/bSlq/~3/7A5Xz-hvCAY/</link>
		<comments>http://www.ryanmwright.com/2011/09/09/active-federation-with-windows-identity-foundation/#comments</comments>
		<pubDate>Fri, 09 Sep 2011 12:18:22 +0000</pubDate>
		<dc:creator>Ryan</dc:creator>
				<category><![CDATA[Windows Identity Foundation]]></category>
		<category><![CDATA[linkedin]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[WIF]]></category>

		<guid isPermaLink="false">http://www.ryanmwright.com/?p=624</guid>
		<description><![CDATA[Sometimes you may need to perform active federation with Windows Identity Foundation (for example, authenticate against an STS such as ADFS from a process such as a windows service or console application). After hours and hours of scouring the sparse &#8230;<p class="read-more"><a href="http://www.ryanmwright.com/2011/09/09/active-federation-with-windows-identity-foundation/">Read more &#187;</a></p>]]></description>
				<content:encoded><![CDATA[<p>Sometimes you may need to perform active federation with Windows Identity Foundation (for example, authenticate against an STS such as ADFS from a process such as a windows service or console application). After hours and hours of scouring the sparse documentation I constructed the solution which is listed below. Note that this implementation uses UsernameMixed (because in this scenario I have the login credentials on hand) but can be modified if need be.</p>
<p>The end result is you can call Login(userName, password) and when the function call completes you will have an IClaimsPrincipal set that contains all your identity information. Note that this is dependent on some configuration entries, namely:</p>
<ul>
<li>CertificateIssuerName &#8211; The name of the certificate issuer</li>
<li>STSThumbprint &#8211; The thumbprint of the STS</li>
<li>STSEndpoint &#8211; The remote endpoint of the STS</li>
<li>STSRelyingPartyEndpoint &#8211; The address of the RP endpoint registered with the STS</li>
<li>DisableRemoteSTSCertificateValidation &#8211; Boolean to enable/disable certificate validation (in case you want to disable it in a local/DEV environment)</li>
</ul>
<p>The process is threefold:</p>
<ul>
<li>Create an X509CertificateValidator to validate the certificate</li>
<li>Create a IssuerNameRegistry to validate the thumbprint</li>
<li>Utilize some code to actually make the call, decrypt the SAML token, and construct an IClaimsPrincipal</li>
</ul>
<p><strong>The Certificate Validator:</strong></p>
<pre class="brush: csharp; title: ; notranslate">
    public class CustomCertificateValidator : X509CertificateValidator
    {
        public CustomCertificateValidator()
        {
        }

        public override void Validate(System.Security.Cryptography.X509Certificates.X509Certificate2 certificate)
        {
            string allowedIssuerKey = &quot;CertificateIssuerName&quot;;

            if (string.IsNullOrEmpty(ConfigurationManager.AppSettings[allowedIssuerKey]))
            {
                throw new ApplicationException(string.Format(&quot;Issuer config key {0} does not exist in the configuration&quot;, allowedIssuerKey));
            }

            if (!certificate.IssuerName.Name.Equals(ConfigurationManager.AppSettings[allowedIssuerKey]))
            {
                throw new ApplicationException(string.Format(&quot;Certificate comes from issuer {0} which is not allowed.&quot;, certificate.IssuerName.Name));
            }
        }
    }
</pre>
<p><strong>The IssuerNameRegistry:</strong></p>
<pre class="brush: csharp; title: ; notranslate">
    public class CustomTrustedIssuerNameRegistry : IssuerNameRegistry
    {
        /// &lt;summary&gt;
        /// 
        /// &lt;/summary&gt;
        public CustomTrustedIssuerNameRegistry()
        {
        }

        /// &lt;summary&gt;
        /// 
        /// &lt;/summary&gt;
        /// &lt;param name=&quot;securityToken&quot;&gt;&lt;/param&gt;
        /// &lt;returns&gt;&lt;/returns&gt;
        public override string GetIssuerName(SecurityToken securityToken)
        {
            X509SecurityToken x509Token = securityToken as X509SecurityToken;
            if (x509Token != null)
            {
                string thumbprintConfigKey = &quot;STSThumbprint&quot;;

                if (string.IsNullOrEmpty(ConfigurationManager.AppSettings[thumbprintConfigKey]))
                {
                    throw new InvalidDataException(string.Format(&quot;Thumbprint config key {0} does not exist in the configuration&quot;, thumbprintConfigKey));
                }
                
                if (String.Equals(x509Token.Certificate.Thumbprint, ConfigurationManager.AppSettings[thumbprintConfigKey]))
                {
                    return x509Token.Certificate.SubjectName.Name;
                }
            }

            throw new SecurityTokenException(&quot;Untrusted issuer.&quot;);
        }

        public override string GetWindowsIssuerName()
        {
            return base.GetWindowsIssuerName();
        }
    }
</pre>
<p><strong>And the main code:</strong></p>
<pre class="brush: csharp; title: ; notranslate">

/// &lt;summary&gt;
/// Login method to be used by processes that are not run through the web and can use a membership
/// provider. This login method will attach an IClaimsPrincipal object to the current thread
/// and all new threads that will be created as part of this app domain.
/// &lt;/summary&gt;
/// &lt;param name=&quot;userName&quot;&gt;User name.&lt;/param&gt;
/// &lt;param name=&quot;password&quot;&gt;Password.&lt;/param&gt;
/// &lt;returns&gt;Whether the login was successful.&lt;/returns&gt;
public static bool Login(string userName, string password)
{
    // Get the previous principal (in case validation fails and we need to restore)
    IPrincipal previousPrincipal = Thread.CurrentPrincipal;
 
    try
    {
        if (System.Threading.Thread.CurrentPrincipal.Identity.IsAuthenticated)
        {
            // Logout if necessary
            Logout();
        }
 
        string endpointKey = &quot;STSEndpoint&quot;;
        string rpKey = &quot;STSRelyingPartyEndpoint&quot;;
        string disableCertValidationKey = &quot;DisableRemoteSTSCertificateValidation&quot;;
 
        // The STS endpoint
        var adfsEndpoint = ConfigurationManager.AppSettings[endpointKey];
 
        // The RP endpoint
        var relyingPartyEndpoint = ConfigurationManager.AppSettings[rpKey];
 
        if (string.IsNullOrEmpty(adfsEndpoint))
        {
            throw new InvalidDataException(string.Format(&quot;Endpoint '{0}' is not defined in the configuration and cannot be resolved in Login()&quot;, endpointKey));
        }
 
        if (string.IsNullOrEmpty(rpKey))
        {
            throw new InvalidDataException(string.Format(&quot;Relying Party '{0}' is not defined in the configuration and cannot be resolved in Login()&quot;, relyingPartyEndpoint));
        }
 
        // Append Login with credentials we have here.
        adfsEndpoint = string.Concat(adfsEndpoint, &quot;services/Trust/13/UsernameMixed/&quot;);
 
        if (!string.IsNullOrEmpty(disableCertValidationKey))
        {
            string disableRemoteSTSCertificateValidation = ConfigurationManager.AppSettings[disableCertValidationKey];
 
            bool disableSTSCertificateValidation;
 
            if (Boolean.TryParse(disableRemoteSTSCertificateValidation, out disableSTSCertificateValidation))
            {
                if (disableSTSCertificateValidation)
                {
                    // Look up the endpoint for this application
                    ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(
                        delegate
                        {
                            return true;
                        });
                }
            }
        }
 
        var factory = new WSTrustChannelFactory(
                new UserNameWSTrustBinding(SecurityMode.TransportWithMessageCredential),
                new EndpointAddress(adfsEndpoint));
 
        // Set the credentials
        factory.Credentials.UserName.UserName = userName;
        factory.Credentials.UserName.Password = password;
 
        // Set the issuer registry to validate the thumbprint against
        FederatedAuthentication.ServiceConfiguration.IssuerNameRegistry = new CustomTrustedIssuerNameRegistry();
        FederatedAuthentication.ServiceConfiguration.CertificateValidator = new CustomCertificateValidator();
 
        var handler = FederatedAuthentication.ServiceConfiguration.SecurityTokenHandlers.Where(e =&gt; e.TokenType == typeof(Saml2SecurityToken)).FirstOrDefault();
        System.Collections.ObjectModel.Collection&lt;Uri&gt; uris = handler.Configuration.AudienceRestriction.AllowedAudienceUris;
 
        if (uris != null &amp;&amp; !uris.Any(u =&gt; u.OriginalString == relyingPartyEndpoint))
        {
            uris.Add(new Uri(relyingPartyEndpoint));
        }
 
        var channel = factory.CreateChannel();
 
        var rst = new RequestSecurityToken
        {
            RequestType = RequestTypes.Issue,
            AppliesTo = new EndpointAddress(relyingPartyEndpoint),
            KeyType = KeyTypes.Bearer
        };
 
        RequestSecurityTokenResponse rstr = null;
 
        GenericXmlSecurityToken token = channel.Issue(rst, out rstr) as GenericXmlSecurityToken;
 
        DecryptAndAttachTokenToPrincipal(token);
    }
    catch (Exception ex)
    {
        if (ex is MessageSecurityException)
        {
            // User validation fails.
            // Consume the MessageSecurityException, restore the previous principal, and return false.
            Thread.CurrentPrincipal = previousPrincipal;
            return false;
        }
        else
        {
            // Bubble up any other exceptions
            throw ex;
        }
    }
 
    return true;
}
 
/// &lt;summary&gt;
/// Decrypts the GenericXmlSecurityToken passed in and attaches the claims identity to the
/// thread's principal as a ClaimsPrincipal
/// &lt;/summary&gt;
/// &lt;param name=&quot;genericXmlSecurityToken&quot;&gt;&lt;/param&gt;
public static void DecryptAndAttachTokenToPrincipal(GenericXmlSecurityToken genericXmlSecurityToken)
{
    var token = FederatedAuthentication.ServiceConfiguration.SecurityTokenHandlers.ReadToken(new XmlTextReader(new StringReader(genericXmlSecurityToken.TokenXml.OuterXml))); 
    var identity = FederatedAuthentication.ServiceConfiguration.SecurityTokenHandlers.ValidateToken(token).First();
 
    // Get the IClaimsPrincipal and attach it to the current thread
    IClaimsPrincipal claimsPrincipal = ClaimsPrincipal.CreateFromIdentity(identity);
    Thread.CurrentPrincipal = claimsPrincipal;
}
</pre>
<script type="text/javascript">(function() {var s = document.createElement('SCRIPT'), s1 = document.getElementsByTagName('SCRIPT')[0];s.type = 'text/javascript';s.async = true;s.src = 'http://widgets.digg.com/buttons.js';s1.parentNode.insertBefore(s, s1);})();</script><a class="DiggThisButton DiggCompact" href="http://digg.com/submit?url=http%3A%2F%2Fwww.ryanmwright.com%2F2011%2F09%2F09%2Factive-federation-with-windows-identity-foundation%2F"></a><img src="http://feeds.feedburner.com/~r/ryanmwright/bSlq/~4/7A5Xz-hvCAY" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.ryanmwright.com/2011/09/09/active-federation-with-windows-identity-foundation/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		<feedburner:origLink>http://www.ryanmwright.com/2011/09/09/active-federation-with-windows-identity-foundation/</feedburner:origLink></item>
		<item>
		<title>Always keep your (jQuery) Promises</title>
		<link>http://feedproxy.google.com/~r/ryanmwright/bSlq/~3/QNeOcUu-q_I/</link>
		<comments>http://www.ryanmwright.com/2011/09/06/always-keep-your-jquery-promises/#comments</comments>
		<pubDate>Tue, 06 Sep 2011 23:11:27 +0000</pubDate>
		<dc:creator>Ryan</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[jQuery]]></category>
		<category><![CDATA[linkedin]]></category>

		<guid isPermaLink="false">http://www.ryanmwright.com/?p=569</guid>
		<description><![CDATA[jQuery Deferreds are a relatively new feature and can be invaluable when coordinating multiple events and handlers. They provide you with an elegant way to register one or more callbacks into callback queues, invoke them, and signal success/failure. Another advantage &#8230;<p class="read-more"><a href="http://www.ryanmwright.com/2011/09/06/always-keep-your-jquery-promises/">Read more &#187;</a></p>]]></description>
				<content:encoded><![CDATA[<p>jQuery Deferreds are a relatively new feature and can be invaluable when coordinating multiple events and handlers. They provide you with an elegant way to register one or more callbacks into callback queues, invoke them, and signal success/failure. Another advantage they have over traditional callbacks is that you can bind a new callback at <em>any</em> point in your code, even after the event has occurred. If the event has already occurred (i.e. the deferred has been resolved) your callback will happen immediately. Never heard of them before? Well, if you have written code similar to this:</p>
<pre class="brush: jscript; title: ; notranslate">
$.ajax(...).done(function() { })
</pre>
<p>then you have already used them! The above is valid because the jqXHR object that gets returned implements the Promise interface as of jQuery 1.5.</p>
<p>Here&#8217;s a high level summary of how Deferreds work:</p>
<ol>
<li>Call $.Deferred with a callback taking the newly created deferred handler as the first parameter. Once the your operation has completed you call resolve() on the deferred handler.</li>
<li>The deferred handler should return a promise by calling promise(). Think of a promise as a lightweight version of deferred. In essence it provides an immutable version of the deferred object, meaning it can query the state of the deferred handler, but it can&#8217;t modify it (for example, it can&#8217;t resolve it).</li>
<li>Pass the promise around to any interested consumers who would like to be notified of the event&#8217;s completion (success or failure)</li>
</ol>
<p>Remember, you can call promise.done(&#8230;) at any point in your application, even after the deferred handler has been resolved, in which case the callback will be invoked immediately. Another interesting feature is that you can also endow any object with an existing deferred&#8217;s promise by calling deferred.promise(objectInstance). Indeed, if you look at the jQuery framework&#8217;s code this is how they attach the promise methods to the jqXHR object.</p>
<p>As you might guess this can become pretty powerful as it allows us to express event chains in almost any way imaginable.</p>
<p>As a simple example, imagine you would like to submit two asynchronous requests and perform a fade out animation at the same time. Once all three have finished a fade in animation should be run. Deferreds along with jQuery&#8217;s $.when() provide an elegant mechanism to achieve this. Take the code below:</p>
<pre class="brush: jscript; title: ; notranslate">
var container = $(&quot;#mycontainer&quot;);
$.when(
	function () {
		return $.Deferred(function (dfd) {
			container.fadeOut('slow', dfd.resolve);
		}).promise();
	}(),
	$.ajax({
		url: 'Path/To/My/Url/1',
		type: 'POST',
		dataType: 'json'
	}),
	$.ajax({
		url: 'Path/To/My/Url/2',
		type: 'POST',
		dataType: 'json'
	})
).done(function (x, data) {
	container.html('Your request has been processed!');
	container.fadeIn('slow');
});
</pre>
<p>As you can see we pass three promises to $.when, one for the fade out animation and two for the ajax operations. </p>
<ul>
<li>The first parameter is a self executing anonymous function that creates the deferred handler and returns the promise. In $.Deferred&#8217;s callback the dfd.resolve function is passed in to fadeOut()&#8217;s callback parameter, which means that once the animation completes the deferred will be resolved.</li>
<li>With regards to the other two parameters we pass to $.when, since the result of $.ajax is a jqXHR object that implements Promise we just pass the value that&#8217;s returned as is.</li>
</ul>
<p>Once all three have completed we replace the content of the container with a friendly message and fade it in. This is obviously a very bare-bones example, and we&#8217;ve only scratched the surface with what can be achieved with deferreds. One thing you&#8217;ll notice is that I haven&#8217;t included any error handling for the $.ajax calls. I&#8217;ll leave that as homework. </p>
<p>That&#8217;s it! Pretty neat, huh?</p>
<script type="text/javascript">(function() {var s = document.createElement('SCRIPT'), s1 = document.getElementsByTagName('SCRIPT')[0];s.type = 'text/javascript';s.async = true;s.src = 'http://widgets.digg.com/buttons.js';s1.parentNode.insertBefore(s, s1);})();</script><a class="DiggThisButton DiggCompact" href="http://digg.com/submit?url=http%3A%2F%2Fwww.ryanmwright.com%2F2011%2F09%2F06%2Falways-keep-your-jquery-promises%2F"></a><img src="http://feeds.feedburner.com/~r/ryanmwright/bSlq/~4/QNeOcUu-q_I" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.ryanmwright.com/2011/09/06/always-keep-your-jquery-promises/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.ryanmwright.com/2011/09/06/always-keep-your-jquery-promises/</feedburner:origLink></item>
		<item>
		<title>Cascading drop down plugin for jQuery</title>
		<link>http://feedproxy.google.com/~r/ryanmwright/bSlq/~3/7Ifs318C2ZE/</link>
		<comments>http://www.ryanmwright.com/2010/10/13/cascading-drop-down-in-jquery/#comments</comments>
		<pubDate>Thu, 14 Oct 2010 01:36:39 +0000</pubDate>
		<dc:creator>Ryan</dc:creator>
				<category><![CDATA[jQuery]]></category>
		<category><![CDATA[Software Development]]></category>
		<category><![CDATA[ASP.NET MVC]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[linkedin]]></category>

		<guid isPermaLink="false">http://www.ryanmwright.com/?p=380</guid>
		<description><![CDATA[I recently wrote a jQuery plugin called jqCascade for cascading drop downs which can be found at: http://plugins.jquery.com/project/jqcascade. It supports several chained cascade modes when a parent list changes to a &#8220;no selection&#8221; item including: - Simply clearing the child &#8230;<p class="read-more"><a href="http://www.ryanmwright.com/2010/10/13/cascading-drop-down-in-jquery/">Read more &#187;</a></p>]]></description>
				<content:encoded><![CDATA[<p>I recently wrote a jQuery plugin called jqCascade for cascading drop downs which can be found at: <a href="http://plugins.jquery.com/project/jqcascade">http://plugins.jquery.com/project/jqcascade</a>. </p>
<p>It supports several chained cascade modes when a parent list changes to a &#8220;no selection&#8221; item including:<br />
- Simply clearing the child drop downs<br />
- Clearing the child drop downs, leaving the default selection item intact<br />
- Clearing and disabling the child drop downs, leaving the default selection item intact<br />
- Hiding the child drop downs</p>
<p>How do you use it?</p>
<pre class="brush: jscript; title: ; notranslate">
        // Cascade when parent changes
        $('#myParentDropDown').cascading({
                dataUrl: 'http://localhost:31393/Home/GetJsonData/',
                parentDropDownId: 'parentDropDownDOMId',
                noSelectionValue: '0',
                noSelectionText: '[Default Selection]'
            });
</pre>
<p>The drop downs can be chained any number of ways. As always, the project can be downloaded <a href='http://content.ryanmwright.com/2010/10/CascadingDropDownDemo.zip'>here</a>. Try it out and let me know what you think, and if there&#8217;s any suggestions for enhancements you would like to see in a future release let me know!</p>
<script type="text/javascript">(function() {var s = document.createElement('SCRIPT'), s1 = document.getElementsByTagName('SCRIPT')[0];s.type = 'text/javascript';s.async = true;s.src = 'http://widgets.digg.com/buttons.js';s1.parentNode.insertBefore(s, s1);})();</script><a class="DiggThisButton DiggCompact" href="http://digg.com/submit?url=http%3A%2F%2Fwww.ryanmwright.com%2F2010%2F10%2F13%2Fcascading-drop-down-in-jquery%2F"></a><img src="http://feeds.feedburner.com/~r/ryanmwright/bSlq/~4/7Ifs318C2ZE" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.ryanmwright.com/2010/10/13/cascading-drop-down-in-jquery/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		<feedburner:origLink>http://www.ryanmwright.com/2010/10/13/cascading-drop-down-in-jquery/</feedburner:origLink></item>
		<item>
		<title>Ceton InfiniTV 4 CableCard Tuner – First Impressions</title>
		<link>http://feedproxy.google.com/~r/ryanmwright/bSlq/~3/B5PDC7wwwoA/</link>
		<comments>http://www.ryanmwright.com/2010/08/22/ceton-infinitv-4-first-impressions/#comments</comments>
		<pubDate>Sun, 22 Aug 2010 21:51:10 +0000</pubDate>
		<dc:creator>Ryan</dc:creator>
				<category><![CDATA[Home Theatre]]></category>
		<category><![CDATA[HTPC]]></category>
		<category><![CDATA[CableCard]]></category>
		<category><![CDATA[Review]]></category>

		<guid isPermaLink="false">http://www.ryanmwright.com/?p=362</guid>
		<description><![CDATA[I just got my InfiniTV 4 installed and setup today and all I can say is wow! I love it! For anyone who hasn&#8217;t seen my previous post it has support for 4 streams, and the HD content looks amazing. &#8230;<p class="read-more"><a href="http://www.ryanmwright.com/2010/08/22/ceton-infinitv-4-first-impressions/">Read more &#187;</a></p>]]></description>
				<content:encoded><![CDATA[<p>I just got my InfiniTV 4 installed and setup today and all I can say is wow! I love it! For anyone who hasn&#8217;t seen my previous post it has support for 4 streams, and the HD content looks amazing. The device is a little pricey, but I definitely recommend it to anyone who wants to get rid of their cable box and have a first class DVR with MediaCenter (i.e. a slick, snappy, integrated interface instead of that clunky old UI the cable co provides). Check out the <a href="http://www.cetoncorp.com/">Ceton</a> website to get pricing information, and remember that as of the time of this writing this card will <em>only</em> work with Windows 7 and Media Center.</p>
<p><strong>The good:</strong><br />
<strong>-</strong> Installation was a breeze. Simply install the card in your PC, install the software from the Ceton website, plug in your M-Card and you&#8217;re good to go! When you run the setup for your TV signal in Media Center it automatically detects your card, runs you through the setup, and gives you all the information you need to activate it. It couldn&#8217;t be simpler.<br />
<strong>-</strong> Four streams!</p>
<p><strong>The bad:</strong><br />
<strong>-</strong> This is nothing bad about the card, more with my cable company. They don&#8217;t have a self installation option, meaning you need to have them come and install the card for you when there&#8217;s really no reason why you can&#8217;t do it yourself. The $20 installation fee isn&#8217;t too bad, but it still seems like a rather pointless fee to pay. Worst part about it is that the technician didn&#8217;t even touch my PC. I installed the card and device, and I ran the setup in Media Center myself. All the technician had to do was call the cable co and give them the device ID to activate it. Guess they need to squeeze every penny they can out of you <img src='http://www.ryanmwright.com/wp-includes/images/smilies/icon_sad.gif' alt=':-(' class='wp-smiley' /> </p>
<p>So what was my first course of action after the installation? Setup a recording schedule for MythBusters and watch Transformers in HD!</p>
<p>If you have any questions about the card that I have not addressed here let me know in the comments section and I will be sure to answer them.</p>
<script type="text/javascript">(function() {var s = document.createElement('SCRIPT'), s1 = document.getElementsByTagName('SCRIPT')[0];s.type = 'text/javascript';s.async = true;s.src = 'http://widgets.digg.com/buttons.js';s1.parentNode.insertBefore(s, s1);})();</script><a class="DiggThisButton DiggCompact" href="http://digg.com/submit?url=http%3A%2F%2Fwww.ryanmwright.com%2F2010%2F08%2F22%2Fceton-infinitv-4-first-impressions%2F"></a><img src="http://feeds.feedburner.com/~r/ryanmwright/bSlq/~4/B5PDC7wwwoA" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.ryanmwright.com/2010/08/22/ceton-infinitv-4-first-impressions/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		<feedburner:origLink>http://www.ryanmwright.com/2010/08/22/ceton-infinitv-4-first-impressions/</feedburner:origLink></item>
		<item>
		<title>Integrating a Legacy DAL with WCF RIA services</title>
		<link>http://feedproxy.google.com/~r/ryanmwright/bSlq/~3/8_u5NMIa34c/</link>
		<comments>http://www.ryanmwright.com/2010/05/26/integrating-a-legacy-dal-with-wcf-ria-services/#comments</comments>
		<pubDate>Thu, 27 May 2010 02:55:30 +0000</pubDate>
		<dc:creator>Ryan</dc:creator>
				<category><![CDATA[Silverlight]]></category>
		<category><![CDATA[linkedin]]></category>
		<category><![CDATA[MEF]]></category>
		<category><![CDATA[RIA Services]]></category>
		<category><![CDATA[WCF]]></category>

		<guid isPermaLink="false">http://www.ryanmwright.com/?p=274</guid>
		<description><![CDATA[You&#8217;ve gotta admit that RIA services is pretty awesome, it&#8217;s the perfect companion to Silverlight. However it&#8217;s relatively cumbersome and messy to get all the bells and whistles working with a legacy DAL (paging, sorting, etc) and in many cases &#8230;<p class="read-more"><a href="http://www.ryanmwright.com/2010/05/26/integrating-a-legacy-dal-with-wcf-ria-services/">Read more &#187;</a></p>]]></description>
				<content:encoded><![CDATA[<p>You&#8217;ve gotta admit that RIA services is pretty awesome, it&#8217;s the perfect companion to Silverlight. However it&#8217;s relatively cumbersome and messy to get all the bells and whistles working with a legacy DAL (paging, sorting, etc) and in many cases it&#8217;s just not feasible to throw out your investment in your existing infrastructure. So what should you do? Well, I have started a project on Codeplex to address this issue (<a href="http://riatodal.codeplex.com/">http://riatodal.codeplex.com/</a>). Rather than go through the code now, I&#8217;ll post on what the framework accomplishes and give some code samples that use it. In subsequent posts I&#8217;ll delve into bits and pieces of it&#8217;s underpinnings.</p>
<p>First, imagine this scenario. You have a DAL and DAL providers. Specifically, let&#8217;s take a look at a user manager/provider like the ones shown below:</p>
<pre class="brush: csharp; title: ; notranslate">
    public class UserManager
    {
        List&lt;User&gt; _users = new List&lt;User&gt;();

        public List&lt;User&gt; GetUsersByNameAndSearch(string name, int userStatusId, string whereClause, string orderBy, int pageIndex, int pageSize)
        {
            // Paginate our fake data source
            return _users.Skip(pageIndex * pageSize).Take(pageSize).ToList();
        }
    }

    public class DalProvider
    {
        private DalProvider()
        {
        }

        private static DalProvider _instance = null;
        public static DalProvider ProviderInstance
        {
            get
            {
                if (_instance == null)
                {
                    _instance = new DalProvider();
                }

                return _instance;
            }
        }

        private UserManager _umInstance = null;
        public UserManager UserManagerInstance
        {
            get
            {
                if (_umInstance == null)
                {
                    _umInstance = new UserManager();
                }

                return _umInstance;
            }
        }
    }
</pre>
<p>Pretty straight forward. There&#8217;s a provider following the singleton pattern, so to get a list of users, we would write something like:</p>
<pre class="brush: csharp; title: ; notranslate">
List&lt;User&gt; users 
= LegacyDal.DalProvider.ProviderInstance.UserManagerInstance.GetUsersByNameAndSearch(...);
</pre>
<p>and from there we can bind that list of users to a grid. But, that&#8217;s not very good for RIA services. Paging would become a nightmare (how do we get the skip/take parameters to our legacy DAL for paging?). And custom where clauses? eww! How do we link these two together and have them play nicely? The answer is to provide a framework similar to the one described below, which is composed of two primary things:</p>
<p>1) A LINQ provider which we can use to query our DAL<br />
2) &#8220;Hints&#8221; on our RIA entities and domain service methods that will tell the LINQ provider how to resolve the appropriate DAL methods.</p>
<p>The best part is the solution will result in you do not having to modify anything in your DAL, it will just link up and work.</p>
<p>So how does it work? First, decorate your RIA entities that are exposed through your domain service with some custom attributes:</p>
<pre class="brush: csharp; title: ; notranslate">
    [EntityMap(typeof(LegacyDal.Entities.User))]
    public class RIAUser
    {
        [Key]
        [FieldMap(&quot;UserName&quot;)]
        public string RiaUserName { get; set; }

        [FieldMap(&quot;FirstName&quot;)]
        public string RiaFirstName { get; set; }

        [FieldMap(&quot;UserStatus&quot;)]
        public string RiaUserStatus { get; set; }
    }
</pre>
<p>Pretty straightforward. Above we are mapping our RIA user to a user entity in our legacy DAL by mapping the entity type and it&#8217;s corresponding fields.</p>
<p>Then, we add some configuration:</p>
<pre class="brush: xml; title: ; notranslate">
  &lt;riaToDalLinqSettings
     resolverType=&quot;Configuration&quot;
     dalProviderInstance=&quot;LegacyDal.DalProvider.ProviderInstance, LegacyDal&quot;
     entityManagerNameFormatString=&quot;{0}ManagerInstance&quot;
    /&gt;
</pre>
<p>The above says we will resolve our DAL information using the current configuration by setting resolverType=&#8221;Configuration&#8221; (we can write our own and export it using MEF if we want, in which case we would set the resolverType=&#8221;Custom&#8221;). The dalProviderInstance string let&#8217;s the framework know the provider instance to use in the DAL. And for the entityManagerNameFormatString it&#8217;s just a format string which would evaluate to[EntityName]ManagerInstance, or &#8220;UserManagerInstance&#8221; if we were looking for a UserManager.</p>
<p>Then, our domain service looks like:</p>
<pre class="brush: csharp; title: ; notranslate">
    [EnableClientAccess]
    public class GetDataService : LegacyDalDomainServiceBase
    {
        // Create the query context
        RiaToDalQueryContext&lt;RIAUser&gt; RIAUserQueryContext;

        public GetDataService()
        {
            // Instantiate the context with this domain service
            RIAUserQueryContext = new RiaToDalQueryContext&lt;RIAUser&gt;(this);
        }

        /// &lt;summary&gt;
        /// MethodMap will map our queryable GetUser() ria method 
        /// to the GetUsersByNameAndSearch() method  in our DAL.
        /// It will look for an overload which takes name and userStatusId parameter.
        /// The full method signature in the legacy DAL looks like:
        ///     GetUsersByNameAndSearch(string name, int userStatusId, 
        ///           string whereClause, string orderBy, int pageIndex, int pageSize)
        /// but by convention any parameter name of:
        ///       whereClause, orderBy, pageIndex, pageSize 
        /// will be excluded from the match and
        /// will be passed data based on the LINQ query that is composed.
        /// &lt;/summary&gt;
        /// &lt;param name=&quot;name&quot;&gt;&lt;/param&gt;
        /// &lt;returns&gt;&lt;/returns&gt;
        [MethodMap(&quot;GetUsersByNameAndSearch&quot;)]
        public IQueryable&lt;RIAUser&gt; GetUser(
            string name,
            int statusId
            )
        {
            // Use some LINQ against our RIA entities. 
            // The Entities property should already filtered by the &quot;name&quot; 
            // and statusId parameters of the current query method,
            // but we can do some additional filtering, and it will be propagated 
            // through to the whereClause in the legacy DAL method.
            var query = from u in RIAUserQueryContext.Entities
                        where (u.RiaFirstName != &quot;Jim&quot; &amp;&amp; u.RiaUserStatus == &quot;Active&quot;) || u.RiaFirstName == &quot;Ronnie&quot;
                        orderby u.RiaFirstName descending
                        select u;

            // Return the query, let RIA services compose 
            // any additional LINQ sent from the client
            // (such as any paging information)
            return query;
        }
    }
</pre>
<p>Woah! What&#8217;s this doing? I&#8217;ll let you figure that out from the comments and the code, but that&#8217;s all that needs to be done to integrate RIA with our legacy DAL, the framework will handle the rest. Here&#8217;s a screenshot of what the Legacy DAL method receives after this LINQ is executed:</p>
<p><a href="http://content.ryanmwright.com/2010/05/debuggingScreenshot.png"><img src="http://www.ryanmwright.com/wp-content/plugins/jquery-image-lazy-loading/images/grey.gif" data-original="http://content.ryanmwright.com/2010/05/debuggingScreenshot-300x29.png" alt="" title="DebuggingScreenshot" width="300" height="29" class="lazy alignnone size-medium wp-image-280" /><noscript><img src="http://content.ryanmwright.com/2010/05/debuggingScreenshot-300x29.png" alt="" title="DebuggingScreenshot" width="300" height="29" class="alignnone size-medium wp-image-280" /></noscript></a></p>
<p>and here&#8217;s what the client that executed the query method looked like:</p>
<p><a href="http://content.ryanmwright.com/2010/05/clientScreenshot.png"><img src="http://www.ryanmwright.com/wp-content/plugins/jquery-image-lazy-loading/images/grey.gif" data-original="http://content.ryanmwright.com/2010/05/clientScreenshot-300x91.png" alt="" title="clientScreenshot" width="300" height="91" class="lazy alignnone size-medium wp-image-281" /><noscript><img src="http://content.ryanmwright.com/2010/05/clientScreenshot-300x91.png" alt="" title="clientScreenshot" width="300" height="91" class="alignnone size-medium wp-image-281" /></noscript></a></p>
<p>Notice how the where clause from our LINQ expression got passed to the where clause parameter in our DAL method? (it even resolved the correct names by translating RIAFirstName to FirstName). The paging parameters came from the DataPager in the Silverlight client, as did the order by clause. The userName and userStatusId were received from the parameters in the  IQueryable<RIAUser> GetUser() domain service. All of this was done without any additional code, we just needed to give the framework some &#8220;hints&#8221; on how to resolve our DAL.</p>
<p>So what&#8217;s this running on? Just MEF, Silverlight 4 and WCF RIA Services. This is a very preliminary design, but I&#8217;m planning on adding:</p>
<ul>
<li>Update support. Call this.Update(entity) in your domain service and based on the mappings it will persist the entity back to the DAL</li>
<li>Transaction support via TransactionScope</li>
<li>Deep loading with the [Associate] attribute</li>
<li>[Ignore] attributes for the query parameters so parameters can be selectively excluded from matching when trying to resolve the appropriate DAL method</li>
<li>Re-factoring the code and making it more plugable</li>
<li>Anything I can think of or that&#8217;s suggested to me!</li>
</ul>
<p>Be sure to take a look on <a href="http://riatodal.codeplex.com/">Codeplex</a> for the source (and a working example), and if you have any suggestions please let me know in the comments section! </p>
<script type="text/javascript">(function() {var s = document.createElement('SCRIPT'), s1 = document.getElementsByTagName('SCRIPT')[0];s.type = 'text/javascript';s.async = true;s.src = 'http://widgets.digg.com/buttons.js';s1.parentNode.insertBefore(s, s1);})();</script><a class="DiggThisButton DiggCompact" href="http://digg.com/submit?url=http%3A%2F%2Fwww.ryanmwright.com%2F2010%2F05%2F26%2Fintegrating-a-legacy-dal-with-wcf-ria-services%2F"></a><img src="http://feeds.feedburner.com/~r/ryanmwright/bSlq/~4/8_u5NMIa34c" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.ryanmwright.com/2010/05/26/integrating-a-legacy-dal-with-wcf-ria-services/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://www.ryanmwright.com/2010/05/26/integrating-a-legacy-dal-with-wcf-ria-services/</feedburner:origLink></item>
		<item>
		<title>Dynamic Controller/Action Authorization in ASP.NET MVC</title>
		<link>http://feedproxy.google.com/~r/ryanmwright/bSlq/~3/hl9-ew17inQ/</link>
		<comments>http://www.ryanmwright.com/2010/04/25/dynamic-controlleraction-authorization-in-asp-net-mvc/#comments</comments>
		<pubDate>Sun, 25 Apr 2010 19:45:53 +0000</pubDate>
		<dc:creator>Ryan</dc:creator>
				<category><![CDATA[ASP.NET MVC]]></category>
		<category><![CDATA[linkedin]]></category>
		<category><![CDATA[Security]]></category>

		<guid isPermaLink="false">http://www.ryanmwright.com/?p=203</guid>
		<description><![CDATA[Note: This has a CodePlex project now: http://mvcauthorization.codeplex.com/. The code there is a little different than the code below, and has some nice new features. The code below should still serve as a good reference, however. Often times, after you&#8217;ve &#8230;<p class="read-more"><a href="http://www.ryanmwright.com/2010/04/25/dynamic-controlleraction-authorization-in-asp-net-mvc/">Read more &#187;</a></p>]]></description>
				<content:encoded><![CDATA[<p><strong>Note: This has a CodePlex project now: <a href="http://mvcauthorization.codeplex.com/">http://mvcauthorization.codeplex.com/</a>. The code there is a little different than the code below, and has some nice new features. The code below should still serve as a good reference, however.</strong></p>
<p>Often times, after you&#8217;ve authenticated your users, you&#8217;ll want to authorize what they actually have control over based on role. Typically in an ASP.NET MVC project this happens by using an authorize attribute not unlike the one shown below:</p>
<pre class="brush: csharp; title: ; notranslate">
[Authorize(Roles = &quot;Programmer, Manager&quot;)]
public ActionResult MyTopSecretActionForSuperCoolPeopleOnly()
</pre>
<p>Unfortunately, the above code directly ties your action and controller code to your user roles. So what happens if a new role is added? Well, in my fake system I just added one, and it&#8217;s called the Fonzie role. Anyone who is at Fonzie status is by definition super cool, and needs to have access to our super cool action. So our code above would become:</p>
<pre class="brush: csharp; title: ; notranslate">
[Authorize(Roles = &quot;Programmer, Manager, Fonzie&quot;)]
public ActionResult MyTopSecretActionForSuperCoolPeopleOnly()
</pre>
<p>But, because our solution is code based we need to wait for a re-deployment and only then will the Fonzies get access to to our action. Not good! Also, what do we do about any action links to that action? We don&#8217;t want them just showing up for any old user. We&#8217;d better address that as well.</p>
<p>The solution to this problem is to create new configuration sections in web.config, which will allow us to specify authorization roles for our actions and controllers in a declarative way. That way, we only need to update the configuration, and the roles are automatically mapped to the actions and controllers without need for re-deployment. Below is what the code using this functionality should ultimately look like.</p>
<p>Define a configuration for controller/action roles:</p>
<pre class="brush: xml; title: ; notranslate">
  &lt;authorizationConfiguration&gt;
    &lt;controllerAuthorizationMappings&gt;
      &lt;add controller=&quot;Home&quot; role=&quot;GeneralAccess&quot;&gt;        
        &lt;!-- Define allowed roles for actions under the Home Controller --&gt;
        &lt;actionAuthorizationMappings&gt;
          &lt;add action=&quot;MyTopSecretActionForSuperCoolPeopleOnly&quot; roles=&quot;Developer,Manager,Fonzie&quot; /&gt;
        &lt;/actionAuthorizationMappings&gt;
      &lt;/add&gt;
    &lt;/controllerAuthorizationMappings&gt;
  &lt;/authorizationConfiguration&gt;
</pre>
<p>The configuration above says we should allow any user with GeneralAccess to the &#8220;Home&#8221; controller, but our top secret action should only be accessible to users with a Developer, Manager, or Fonzie role. Then to use it, we would just need to decorate our action with a new attribute called ActionAuthorize, which is derived from AuthorizeAttribute, and needs no parameters:</p>
<pre class="brush: csharp; title: ; notranslate">
[ActionAuthorize]
public ActionResult MyTopSecretActionForSuperCoolPeopleOnly()
</pre>
<p>And the new attribute would be smart enough to look-up the roles for this action and allow/disallow access based on the configuration. (Note that in addition to [ActionAuthorize], we also have an attribute called [ControllerAuthorize] which does the same thing for controllers).</p>
<p>When we link to this action, we would need to have a custom html helper that only renders the action link when the user is in role for that action. The usage would look like:</p>
<pre class="brush: csharp; title: ; notranslate">
&lt;%= Html.SecuredActionLink(&quot;Secure Link.&quot;, &quot;MyTopSecretActionForSuperCoolPeopleOnly&quot;) %&gt;
</pre>
<p>To accomplish the above we will need to start off with five classes for our configuration section:</p>
<p><strong>AuthorizationConfiguration</strong><br />
This class defines the root or our configuration section and contains a static accessor property, as well as a ControllerAuthorizationConfigurationCollection.</p>
<pre class="brush: csharp; title: ; notranslate">
    public class AuthorizationConfiguration : ConfigurationSection
    {
        private static AuthorizationConfiguration _authorizationConfiguration
            = ConfigurationManager.GetSection(&quot;authorizationConfiguration&quot;) as AuthorizationConfiguration;

        public static AuthorizationConfiguration Section
        {
            get
            {
                return _authorizationConfiguration;
            }
        }

        [ConfigurationProperty(&quot;controllerAuthorizationMappings&quot;)]
        public ControllerAuthorizationConfigurationCollection ControllerAuthorizationMappings
        {
            get
            {
                return this[&quot;controllerAuthorizationMappings&quot;] as ControllerAuthorizationConfigurationCollection;
            }
        }
    }
</pre>
<pre class="brush: csharp; title: ; notranslate">

</pre>
<p><strong>ControllerAuthorizationConfigurationCollection</strong><br />
This class will encapsulate our collection of Controller configurations.</p>
<pre class="brush: csharp; title: ; notranslate">
   public class ControllerAuthorizationConfigurationCollection : ConfigurationElementCollection, 

IEnumerable&lt;ControllerAuthorizationConfigurationElement&gt;
    {
        /// &lt;summary&gt;
        /// 
        /// &lt;/summary&gt;
        /// &lt;param name=&quot;index&quot;&gt;&lt;/param&gt;
        /// &lt;returns&gt;&lt;/returns&gt;
        public ControllerAuthorizationConfigurationElement this[int index]
        {
            get
            {
                return base.BaseGet(index) as ControllerAuthorizationConfigurationElement;
            }
            set
            {
                if (base.BaseGet(index) != null)
                {
                    base.BaseRemoveAt(index);
                }
                this.BaseAdd(index, value);
            }
        }

        /// &lt;summary&gt;
        /// 
        /// &lt;/summary&gt;
        public IEnumerable&lt;ControllerAuthorizationConfigurationElement&gt; Elements
        {
            get
            {
                for (int i = 0; i &lt; base.Count; ++i)
                {
                    yield return this[i];
                }
            }
        }

        /// &lt;summary&gt;
        /// 
        /// &lt;/summary&gt;
        /// &lt;returns&gt;&lt;/returns&gt;
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        /// &lt;summary&gt;
        /// 
        /// &lt;/summary&gt;
        /// &lt;returns&gt;&lt;/returns&gt;
        public new IEnumerator&lt;ControllerAuthorizationConfigurationElement&gt; GetEnumerator()
        {
            for (int i = 0; i &lt; base.Count; ++i)
            {
                yield return this[i];
            }
        }

        /// &lt;summary&gt;
        /// 
        /// &lt;/summary&gt;
        /// &lt;returns&gt;&lt;/returns&gt;
        protected override ConfigurationElement CreateNewElement()
        {
            return new ControllerAuthorizationConfigurationElement();
        }

        /// &lt;summary&gt;
        /// 
        /// &lt;/summary&gt;
        /// &lt;param name=&quot;element&quot;&gt;&lt;/param&gt;
        /// &lt;returns&gt;&lt;/returns&gt;
        protected override object GetElementKey(ConfigurationElement element)
        {
            return ((ControllerAuthorizationConfigurationElement)element).Controller;
        } 
    }
</pre>
<p><strong>ControllerAuthorizationConfigurationElement</strong></p>
<p>This class will define the base configuration element for a controller. It has an optional property called &#8220;Roles&#8221; which we can use to lock down a controller by a certain set of roles. It also has an actionAuthorizationConfigurationCollection to maintain a list of actions.</p>
<pre class="brush: csharp; title: ; notranslate">
    public class ControllerAuthorizationConfigurationElement : ConfigurationElement
    {
        [ConfigurationProperty(&quot;controller&quot;, IsRequired = true)]
        public string Controller
        {
            get
            {
                return (string)this[&quot;controller&quot;];
            }
            set
            {
                this[&quot;controller&quot;] = value;
            }
        }

        [ConfigurationProperty(&quot;roles&quot;, IsRequired = false)]
        public string Roles
        {
            get
            {
                return (string)this[&quot;roles&quot;];
            }
            set
            {
                this[&quot;roles&quot;] = value;
            }
        }

        [ConfigurationProperty(&quot;actionAuthorizationMappings&quot;)]
        public ActionAuthorizationConfigurationCollection ActionAuthorizationMappings
        {
            get
            {
                return this[&quot;actionAuthorizationMappings&quot;] as ActionAuthorizationConfigurationCollection;
            }
        }
    }
</pre>
<p><strong>ActionAuthorizationConfigurationCollection</strong><br />
This class will encapsulate our collection of Controller configurations under the current controller.</p>
<pre class="brush: csharp; title: ; notranslate">
   public class ActionAuthorizationConfigurationCollection : ConfigurationElementCollection, 

IEnumerable&lt;ActionAuthorizationConfigurationElement&gt;
    {
        /// &lt;summary&gt;
        /// 
        /// &lt;/summary&gt;
        /// &lt;param name=&quot;index&quot;&gt;&lt;/param&gt;
        /// &lt;returns&gt;&lt;/returns&gt;
        public ActionAuthorizationConfigurationElement this[int index]
        {
            get
            {
                return base.BaseGet(index) as ActionAuthorizationConfigurationElement;
            }
            set
            {
                if (base.BaseGet(index) != null)
                {
                    base.BaseRemoveAt(index);
                }
                this.BaseAdd(index, value);
            }
        }

        /// &lt;summary&gt;
        /// 
        /// &lt;/summary&gt;
        public IEnumerable&lt;ActionAuthorizationConfigurationElement&gt; Elements
        {
            get
            {
                for (int i = 0; i &lt; base.Count; ++i)
                {
                    yield return this[i];
                }
            }
        }

        /// &lt;summary&gt;
        /// 
        /// &lt;/summary&gt;
        /// &lt;returns&gt;&lt;/returns&gt;
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        /// &lt;summary&gt;
        /// 
        /// &lt;/summary&gt;
        /// &lt;returns&gt;&lt;/returns&gt;
        public new IEnumerator&lt;ActionAuthorizationConfigurationElement&gt; GetEnumerator()
        {
            for (int i = 0; i &lt; base.Count; ++i)
            {
                yield return this[i];
            }
        }

        /// &lt;summary&gt;
        /// 
        /// &lt;/summary&gt;
        /// &lt;returns&gt;&lt;/returns&gt;
        protected override ConfigurationElement CreateNewElement()
        {
            return new ActionAuthorizationConfigurationElement();
        }

        /// &lt;summary&gt;
        /// 
        /// &lt;/summary&gt;
        /// &lt;param name=&quot;element&quot;&gt;&lt;/param&gt;
        /// &lt;returns&gt;&lt;/returns&gt;
        protected override object GetElementKey(ConfigurationElement element)
        {
            return ((ActionAuthorizationConfigurationElement)element).Action;
        } 
    }
</pre>
<p><strong>ActionAuthorizationConfigurationElement</strong><br />
This class will encapsulate a particular action under a controller. It has a required &#8220;Roles&#8221; parameter.</p>
<pre class="brush: csharp; title: ; notranslate">
    public class ActionAuthorizationConfigurationElement : ConfigurationElement
    {
        [ConfigurationProperty(&quot;action&quot;, IsRequired = true)]
        public string Action
        {
            get
            {
                return (string)this[&quot;action&quot;];
            }
            set
            {
                this[&quot;action&quot;] = value;
            }
        }

        [ConfigurationProperty(&quot;roles&quot;, IsRequired = true)]
        public string Roles
        {
            get
            {
                return (string)this[&quot;roles&quot;];
            }
            set
            {
                this[&quot;roles&quot;] = value;
            }
        }
    }
</pre>
<p>After we have defined our configuration elements and registered the section in our web.config, we can start using it. We define two attribute classes which will perform the authentication look-up for our controllers and actions:</p>
<p><strong>ActionAuthorizeAttribute</strong> targets an action method:</p>
<pre class="brush: csharp; title: ; notranslate">
    /// &lt;summary&gt;
    /// Authorization attribute for an MVC action method
    /// &lt;/summary&gt;
    [AttributeUsage(AttributeTargets.Method)]
    public class ActionAuthorizeAttribute : AuthorizeAttribute
    {
        private string[] _rolesArray;
        private string _authorizationGroupName;

        /// &lt;summary&gt;
        /// 
        /// &lt;/summary&gt;
        public string AccessDeniedController { get; set; }

        /// &lt;summary&gt;
        /// 
        /// &lt;/summary&gt;
        public string AccessDeniedAction { get; set; }

        /// &lt;summary&gt;
        /// 
        /// &lt;/summary&gt;
        public ActionAuthorizeAttribute()
        {
            AccessDeniedController = ConfigurationManager.AppSettings[&quot;DefaultAccessDeniedController&quot;] ?? 

&quot;AccessDenied&quot;;
            AccessDeniedAction = ConfigurationManager.AppSettings[&quot;DefaultAccessDeniedAction&quot;] ?? &quot;Index&quot;;
            _authorizationGroupName = string.Empty;
        }

        /// &lt;summary&gt;
        /// 
        /// &lt;/summary&gt;
        public string[] RolesArray
        {
            get
            {
                return _rolesArray;
            }
            set
            {
                _rolesArray = value;
                this.Roles = string.Join(&quot;,&quot;, _rolesArray);
            }
        }

        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            string controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;

            // Get any controller specific role mappings
            var controllerRoleMappings = AuthorizationConfiguration.Section.ControllerAuthorizationMappings.Where(e 

=&gt; e.Controller == controllerName).FirstOrDefault();

            if (controllerRoleMappings != null)
            {
                var actionRoleMappings = controllerRoleMappings.ActionAuthorizationMappings.Where(e =&gt; e.Action == 

filterContext.ActionDescriptor.ActionName).FirstOrDefault();

                if (actionRoleMappings != null &amp;&amp; !string.IsNullOrEmpty(actionRoleMappings.Roles))
                {
                    this.RolesArray = actionRoleMappings.Roles.Split(',');
                }
            }

            //
            base.OnAuthorization(filterContext);
        }

        protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
        {
            filterContext.Result = new RedirectToRouteResult(new System.Web.Routing.RouteValueDictionary(new { 

Controller = AccessDeniedController, Action = AccessDeniedAction, Roles = Roles }));
        }
    }
</pre>
<p><strong>ControllerAuthorizeAttribute</strong> targets a controller class:</p>
<pre class="brush: csharp; title: ; notranslate">
    /// &lt;summary&gt;
    /// Authorization attribute for an MVC controller
    /// &lt;/summary&gt;
    [AttributeUsage(AttributeTargets.Class)]
    public class ControllerAuthorizeAttribute : AuthorizeAttribute
    {
        private string[] _rolesArray;
        private string _authorizationGroupName;

        /// &lt;summary&gt;
        /// 
        /// &lt;/summary&gt;
        public string AccessDeniedController { get; set; }

        /// &lt;summary&gt;
        /// 
        /// &lt;/summary&gt;
        public string AccessDeniedAction { get; set; }

        /// &lt;summary&gt;
        /// 
        /// &lt;/summary&gt;
        public ControllerAuthorizeAttribute()
        {
            AccessDeniedController = ConfigurationManager.AppSettings[&quot;DefaultAccessDeniedController&quot;] ?? 

&quot;AccessDenied&quot;;
            AccessDeniedAction = ConfigurationManager.AppSettings[&quot;DefaultAccessDeniedAction&quot;] ?? &quot;Index&quot;;
            _authorizationGroupName = string.Empty;
        }

        /// &lt;summary&gt;
        /// 
        /// &lt;/summary&gt;
        public string[] RolesArray
        {
            get
            {
                return _rolesArray;
            }
            set
            {
                _rolesArray = value;
                this.Roles = string.Join(&quot;,&quot;, _rolesArray);
            }
        }

        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            string controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;

            // Get any controller specific role mappings
            var controllerRoleMappings = AuthorizationConfiguration.Section.ControllerAuthorizationMappings.Where(e 

=&gt; e.Controller == controllerName).FirstOrDefault();

            if (controllerRoleMappings != null &amp;&amp; !string.IsNullOrEmpty(controllerRoleMappings.Roles))
            {
                this.RolesArray = controllerRoleMappings.Roles.Split(',');
            }

            //
            base.OnAuthorization(filterContext);
        }

        protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
        {
            filterContext.Result = new RedirectToRouteResult(new System.Web.Routing.RouteValueDictionary(new { 

Controller = AccessDeniedController, Action = AccessDeniedAction, Roles = Roles }));
        }
    }
</pre>
<p>Then for dynamic action link rendering based on role, the following Html helper would be as follows:</p>
<pre class="brush: csharp; title: ; notranslate">

        /// &lt;summary&gt;
        /// Renders the action link, provided the user has access to the action method being linked to
        /// &lt;/summary&gt;
        /// &lt;param name=&quot;helper&quot;&gt;&lt;/param&gt;
        /// &lt;param name=&quot;linkText&quot;&gt;&lt;/param&gt;
        /// &lt;param name=&quot;actionName&quot;&gt;&lt;/param&gt;
        /// &lt;returns&gt;&lt;/returns&gt;
        public static MvcHtmlString SecuredActionLink(this HtmlHelper helper, string linkText, string actionName)
        {
            MvcHtmlString html = MvcHtmlString.Empty;
            string[] roles = null;

            RouteData routeData = helper.ViewContext.RouteData;
            string controllerName = routeData.GetRequiredString(&quot;controller&quot;);

            // Get any controller specific role mappings
            var controllerRoleMappings = AuthorizationConfiguration.Section.ControllerAuthorizationMappings.Where(e 

=&gt; e.Controller == controllerName).FirstOrDefault();
            ActionAuthorizationConfigurationElement actionRoleMappings = null;

            if (controllerRoleMappings != null)
            {
                actionRoleMappings = controllerRoleMappings.ActionAuthorizationMappings.Where(e =&gt; e.Action == 

actionName).FirstOrDefault();

                if (actionRoleMappings != null &amp;&amp; !string.IsNullOrEmpty(actionRoleMappings.Roles))
                {
                    roles = actionRoleMappings.Roles.Split(',');
                }
            }

            if (roles != null)
            {
                foreach (string role in roles)
                {
                    if (ApplicationUser.Current.Roles.Contains(role))
                    {
                        html = System.Web.Mvc.Html.LinkExtensions.ActionLink(helper, linkText, actionName);
                        break;
                    }
                }
            }
            else if (actionRoleMappings == null)
            {
                // If there's no roles associated to that action then render the link
                html = System.Web.Mvc.Html.LinkExtensions.ActionLink(helper, linkText, actionName);
            }

            return html;
        }
    }
</pre>
<p>The helper above will figure out if the user has access to the action, display accordingly, and viola! Users will never see a link to an action for which they do not have access, and if they do somehow get to an action that is restricted based on their role they will get redirected to an access denied page of your choosing.</p>
<p>This is just a basic sample, but there&#8217;s lots that can be done in addition to this. Be sure to download the source <a href='http://content.ryanmwright.com/2010/04/MvcApplication.zip'>here</a> to see it in action (VS 2010, ASP.NET MVC 2, and WIF required). In particular play around with the role definitions at the bottom of the web.config to see the actions dynamically authorize as the roles in the config change. The authentication is done via the default STS server project that gets created when you create a federated security application, so you will need to install the WIF RTW and SDK in order to successfully run the project the way it is.</p>
<script type="text/javascript">(function() {var s = document.createElement('SCRIPT'), s1 = document.getElementsByTagName('SCRIPT')[0];s.type = 'text/javascript';s.async = true;s.src = 'http://widgets.digg.com/buttons.js';s1.parentNode.insertBefore(s, s1);})();</script><a class="DiggThisButton DiggCompact" href="http://digg.com/submit?url=http%3A%2F%2Fwww.ryanmwright.com%2F2010%2F04%2F25%2Fdynamic-controlleraction-authorization-in-asp-net-mvc%2F"></a><img src="http://feeds.feedburner.com/~r/ryanmwright/bSlq/~4/hl9-ew17inQ" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.ryanmwright.com/2010/04/25/dynamic-controlleraction-authorization-in-asp-net-mvc/feed/</wfw:commentRss>
		<slash:comments>12</slash:comments>
		<feedburner:origLink>http://www.ryanmwright.com/2010/04/25/dynamic-controlleraction-authorization-in-asp-net-mvc/</feedburner:origLink></item>
		<item>
		<title>The .NET Facebook API and Silverlight</title>
		<link>http://feedproxy.google.com/~r/ryanmwright/bSlq/~3/Tf4eIMIgoww/</link>
		<comments>http://www.ryanmwright.com/2009/12/24/the-net-facebook-api-and-silverlight/#comments</comments>
		<pubDate>Thu, 24 Dec 2009 05:07:13 +0000</pubDate>
		<dc:creator>Ryan</dc:creator>
				<category><![CDATA[Silverlight]]></category>
		<category><![CDATA[Facebook]]></category>
		<category><![CDATA[linkedin]]></category>

		<guid isPermaLink="false">http://www.ryanmwright.com/?p=159</guid>
		<description><![CDATA[Awesome! There is now a library for .NET developers to interface with the Facebook services, opening up a whole new world of integration between your app and all of your user&#8217;s information in Facebook. In this post I have included &#8230;<p class="read-more"><a href="http://www.ryanmwright.com/2009/12/24/the-net-facebook-api-and-silverlight/">Read more &#187;</a></p>]]></description>
				<content:encoded><![CDATA[<p>Awesome! There is now a library for .NET developers to interface with the Facebook services, opening up a whole new world of integration between your app and all of your user&#8217;s information in Facebook. In this post I have included code samples for a simple Facebook friend viewer. With the viewer, you can pull all tagged photos of yourself, your friends, and all their information. While the app functions, there is so much more that can be done. Comments can be posted to Facebook remotely, pictures uploaded and tagged remotely, videos uploaded, etc. Virtually all of the Facebook functionality is exposed through the API. Checkout the bottom of the post for a link to the project, as well as a working demo. Let&#8217;s dive in!</p>
<p>First, <a href="http://facebooktoolkit.codeplex.com/">downoad the required assemblies</a>. Note that there are assemblies for WinForms, WPF, ASP.NET MVC (not just Silverlight) included!</p>
<p>Then, you will need to setup your account at <a href="http://www.facebook.com/developers/">http://www.facebook.com/developers/</a>. Click on &#8220;Setup New Application&#8221; and enter a new application name as shown in the screenshot below:</p>
<p><a href="http://content.ryanmwright.com/2009/12/CreateApp.PNG"><img src="http://www.ryanmwright.com/wp-content/plugins/jquery-image-lazy-loading/images/grey.gif" data-original="http://content.ryanmwright.com/2009/12/CreateApp-300x129.PNG" alt="CreateApp" title="CreateApp" width="300" height="129" class="lazy alignnone size-medium wp-image-160" /><noscript><img src="http://content.ryanmwright.com/2009/12/CreateApp-300x129.PNG" alt="CreateApp" title="CreateApp" width="300" height="129" class="alignnone size-medium wp-image-160" /></noscript></a></p>
<p>Then fill out the fields shown below on the &#8220;Basic Info&#8221; tab. Note the developers area. Add any devs who will be working on the app there:</p>
<p><a href="http://content.ryanmwright.com/2009/12/FillOutInfo.PNG"><img src="http://www.ryanmwright.com/wp-content/plugins/jquery-image-lazy-loading/images/grey.gif" data-original="http://content.ryanmwright.com/2009/12/FillOutInfo-300x258.PNG" alt="FillOutInfo" title="FillOutInfo" width="300" height="258" class="lazy alignnone size-medium wp-image-200" /><noscript><img src="http://content.ryanmwright.com/2009/12/FillOutInfo-300x258.PNG" alt="FillOutInfo" title="FillOutInfo" width="300" height="258" class="alignnone size-medium wp-image-200" /></noscript></a></p>
<p>From there fill out the connect information. In development this will be the local http://localhost:[port] that that you are developing on. In production it will be yourdomain.com:</p>
<p><a href="http://content.ryanmwright.com/2009/12/connect.PNG"><img src="http://www.ryanmwright.com/wp-content/plugins/jquery-image-lazy-loading/images/grey.gif" data-original="http://content.ryanmwright.com/2009/12/connect-300x110.PNG" alt="connect" title="connect" width="300" height="110" class="lazy alignnone size-medium wp-image-163" /><noscript><img src="http://content.ryanmwright.com/2009/12/connect-300x110.PNG" alt="connect" title="connect" width="300" height="110" class="alignnone size-medium wp-image-163" /></noscript></a></p>
<p>Then click on the advanced tab and enable Sandbox mode, as we will be using this account for development purposes:</p>
<p><a href="http://content.ryanmwright.com/2009/12/advancedSandbox.PNG"><img src="http://www.ryanmwright.com/wp-content/plugins/jquery-image-lazy-loading/images/grey.gif" data-original="http://content.ryanmwright.com/2009/12/advancedSandbox-300x104.PNG" alt="advancedSandbox" title="advancedSandbox" width="300" height="104" class="lazy alignnone size-medium wp-image-165" /><noscript><img src="http://content.ryanmwright.com/2009/12/advancedSandbox-300x104.PNG" alt="advancedSandbox" title="advancedSandbox" width="300" height="104" class="alignnone size-medium wp-image-165" /></noscript></a></p>
<p></p>
<p>Cool! Now that we have our account setup we need to create a &#8220;Silverlight Navigation Application&#8221; in Visual Studio. To make everything work, we need to add a javascript file to the same directory as the HTML page that is hosting our Silverlight app. This will allow us to facilitate the cross domain communication between our app and Facebook (note the var silverLightPluginId must match the DOM id of the Silverlight plugin on the page!!!):</p>
<p>fblogin.js</p>
<pre class="brush: jscript; title: ; notranslate">
//must match the Silverlight plugin's DOM ID
var silverlightPluginId = 'Silverlight1';

function facebook_init(appid) {
    FB.init(appid, &quot;/xd_receiver.htm&quot;);
}

function isUserConnected() {
    FB.ensureInit(function () {
        FB.Connect.get_status().waitUntilReady(function (status) {
            var plugin = document.getElementById(silverlightPluginId);
        });
    });
}

function facebook_login() {
    FB.ensureInit(function () {
        FB.Connect.requireSession(facebook_getSession, true);
    });
}

function facebook_logout() {
    FB.Connect.logout(facebook_onlogout);
}

function facebook_getSession() {

    FB.Facebook.get_sessionState().waitUntilReady(function () {
        var session = FB.Facebook.apiClient.get_session();
        var plugin = document.getElementById(silverlightPluginId);
        plugin.Content.FacebookLoginControl.LoggedIn(session.session_key, session.secret, session.expires, session.uid);
    });
}

function facebook_onlogout() {
    var plugin = document.getElementById(silverlightPluginId);
    plugin.Content.FacebookLoginControl.LoggedOut();
}

function facebook_onpermission(accepted) {
    var plugin = document.getElementById(silverlightPluginId);
    plugin.Content.FacebookLoginControl.PermissionCallback(accepted);
}

function facebook_prompt_permission(permission) {
    FB.ensureInit(function () {
        FB.Connect.showPermissionDialog(permission, facebook_onpermission);
    });
}
</pre>
<p>Include the script in the page hosting the Silverlight app:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;script src=&quot;http://static.ak.connect.facebook.com/js/api_lib/v0.4/FeatureLoader.js.php&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;fblogin.js&quot;&gt;&lt;/script&gt;
</pre>
<p>Now, add the file xd_receiver.htm to the same directory as the javascript file. This will receive the response from Facebook and route it appropriately:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 Strict//EN&quot; 
     &quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd&quot;&gt;
&lt;html&gt;
  &lt;body&gt;
    &lt;script src=http://static.ak.connect.facebook.com/js/api_lib/v0.4/XdCommReceiver.js
                    type=&quot;text/javascript&quot;&gt;
    &lt;/script&gt;
  &lt;/body&gt;
&lt;/html&gt;
</pre>
<p>Whew! Now that all that&#8217;s done we can tackle the Silverlight part. Add a reference the assembly Facebook.Silverlight.dll to your Silverlight project. Note that if you are using Visual Studio 2010 you might get a build error similar to the following after you add the reference:</p>
<p>—> System.NotSupportedException: An attempt was made to load an assembly from a network location which would have caused the assembly to be sandboxed in previous versions of the .NET Framework. This release of the .NET Framework does not enable CAS policy by default, so this load may be dangerous. If this load is not intended to sandbox the assembly, please enable the loadFromRemoteSources switch. See http://go.microsoft.com/fwlink/?LinkId=155569 for more information.</p>
<p>This is because the CAS policies have changed in VS2010. No worries though, just locate the assembly in the file system and Unblock it by clicking on the button in the screenshot below:</p>
<p><a href="http://content.ryanmwright.com/2009/12/UnBlock.PNG"><img src="http://www.ryanmwright.com/wp-content/plugins/jquery-image-lazy-loading/images/grey.gif" data-original="http://content.ryanmwright.com/2009/12/UnBlock-300x171.PNG" alt="UnBlock" title="UnBlock" width="300" height="171" class="lazy alignnone size-medium wp-image-171" /><noscript><img src="http://content.ryanmwright.com/2009/12/UnBlock-300x171.PNG" alt="UnBlock" title="UnBlock" width="300" height="171" class="alignnone size-medium wp-image-171" /></noscript></a></p>
<p>In your Silverlight application you then just need to initialize the Facebook API object and invoke the Login() method:</p>
<pre class="brush: csharp; title: ; notranslate">

        private Api _facebookAPI;
        private readonly BrowserSession _browserSession;
        private const string ApplicationKey = &quot;[Put your application key here, yes I know it's not the best place to initialize it!]&quot;;

        public Home()
        {
            InitializeComponent();

            _browserSession = new BrowserSession(ApplicationKey);
            _browserSession.LoginCompleted += browserSession_LoginCompleted;
            _browserSession.GetStatusCompleted += new EventHandler&lt;GetStatusCompletedEventArgs&gt;(_browserSession_GetStatusCompleted);
            _browserSession.Login();
        }
</pre>
<p>This will generate the browser popup to have the user login to their Facebook account. Once the user is logged in the _browserSession_GetStatusCompleted method gets invoked:</p>
<pre class="brush: csharp; title: ; notranslate">
        private void browserSession_LoginCompleted(object sender, EventArgs e)
        {
            _facebookAPI = new Api(_browserSession);

            //set the photo and friends list item sources
            Photos.ItemsSource = myImages;
            FriendsList.ItemsSource = myFriends;

            //get my friends
            _facebookAPI.Friends.GetUserObjectsAsync(
                (friends, state, ex) =&gt;
                {
                    if (ex == null)
                    {
                        Dispatcher.BeginInvoke(
                            () =&gt;
                            {
                                AddFriends(friends);
                            });
                    }
                }, null);

            Facebook.Session.BrowserSession session = sender as Facebook.Session.BrowserSession;

            _facebookAPI.Photos.GetAsync(session.UserId.ToString(), null, null,
                new Photos.GetCallback(
                    (photos, obj, ex) =&gt;
                    {
                        Dispatcher.BeginInvoke(
                            () =&gt;
                            {
                                AddPhotos(photos);
                            });
                    }), null);
        }
</pre>
<p>In here we&#8217;re doing a couple things. First we&#8217;re invoking the FB-API and getting the user&#8217;s friends and a listing of all photos they have been tagged in. Once we have those we add them to the myImages and myFriends observable collections, which are bound to the UI. The AddFriends() and AddPhotos() implementations are listed below:</p>
<pre class="brush: csharp; title: ; notranslate">

        //add friends to the observable collection
        private void AddFriends(IList&lt;Facebook.Schema.user&gt; friends)
        {
            foreach (Facebook.Schema.user friend in friends)
            {
                myFriends.Add(new Friend()
                {
                    Name = friend.first_name,
                    Photo = new BitmapImage(new Uri(friend.pic_small)),
                    UserId = friend.uid.GetValueOrDefault(),
                    Activities = friend.activities,
                    Affiliations = friend.affiliations != null &amp;&amp; friend.affiliations.affiliation != null &amp;&amp; friend.affiliations.affiliation.Count &gt; 0 ? friend.affiliations.affiliation.Select(a =&gt; a.name).Aggregate((i, j) =&gt; string.Concat(i, &quot;, &quot;, j)) : string.Empty,
                    Books = friend.books,
                    EducationHistory = friend.education_history != null &amp;&amp; friend.education_history.education_info != null ? GetEducation(friend.education_history) : string.Empty,
                    Interests = friend.interests,
                    Movies = friend.movies,
                    Music = friend.music,
                    Quotes = friend.quotes,
                    Sex = friend.sex,
                    TV = friend.tv,
                    WorkHistory = friend.work_history != null &amp;&amp; friend.work_history.work_info != null &amp;&amp; friend.work_history.work_info.Count &gt; 0 ? friend.work_history.work_info.Select(e =&gt; e.company_name).Aggregate((i, j) =&gt; string.Concat(i, &quot;, &quot;, j)) : string.Empty
                });
            }
        }

        private void AddPhotos(IList&lt;Facebook.Schema.photo&gt; photos)
        {
            foreach (Facebook.Schema.photo photo in photos)
            {
                myImages.Add(
                        new Photo() { 
                            Small = new BitmapImage(new Uri(photo.src_small)),
                            Normal = new BitmapImage(new Uri(photo.src)),
                            Large = new BitmapImage(new Uri(photo.src_big))
                        }
                    );
            }
        }


        /// &lt;summary&gt;
        /// gets a string representing all education history
        /// &lt;/summary&gt;
        /// &lt;param name=&quot;educationHistory&quot;&gt;&lt;/param&gt;
        /// &lt;returns&gt;&lt;/returns&gt;
        private string GetEducation(Facebook.Schema.userEducation_history educationHistory)
        {
            return educationHistory.education_info.Select(
                    (e) =&gt;
                    {
                        return string.Format(&quot; {0} {1} {2} {3}&quot;, 
                            //degree info
                           !string.IsNullOrEmpty(e.degree) ? &quot;Degree: &quot; + e.degree : string.Empty, 

                            //concentration info
                            e.concentrations != null &amp;&amp; e.concentrations.concentration != null &amp;&amp; e.concentrations.concentration.Count &gt; 0
                            ? 
                                &quot;Concentrations: &quot; + e.concentrations.concentration.Aggregate(
                                (i, j) =&gt; {
                                    return string.Concat(i, &quot;, &quot;, j);
                                }) : string.Empty, 
                            
                            //college name
                            !string.IsNullOrEmpty(e.name) ? &quot;College Name: &quot; + e.name : string.Empty, 
                            
                            //college year
                            e.year &gt; 0 ? &quot;Year Graduated: &quot; + e.year : string.Empty);
                    }).Aggregate((i, j) =&gt;
                    {
                        return string.Concat(i, j);
                    });
        }

</pre>
<p>That&#8217;s the core code! Now our UI just needs to bind to the collections and we have all the data we need. Check it out!</p>
<p><a href="http://www.ryanmwright.com/SilverlightDemo/FacebookDemoTestPage.htm"><br />
Working Demo (If your firewall blocks requests to/from Facebook this will just hang and will not load!!)</a></p>
<p><a href='http://content.ryanmwright.com/2009/12/FacebookDemo.zip'>Download the project to get the fully working solution (in VS2010 and Silverlight 4)</a></p>
<script type="text/javascript">(function() {var s = document.createElement('SCRIPT'), s1 = document.getElementsByTagName('SCRIPT')[0];s.type = 'text/javascript';s.async = true;s.src = 'http://widgets.digg.com/buttons.js';s1.parentNode.insertBefore(s, s1);})();</script><a class="DiggThisButton DiggCompact" href="http://digg.com/submit?url=http%3A%2F%2Fwww.ryanmwright.com%2F2009%2F12%2F24%2Fthe-net-facebook-api-and-silverlight%2F"></a><img src="http://feeds.feedburner.com/~r/ryanmwright/bSlq/~4/Tf4eIMIgoww" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.ryanmwright.com/2009/12/24/the-net-facebook-api-and-silverlight/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://www.ryanmwright.com/2009/12/24/the-net-facebook-api-and-silverlight/</feedburner:origLink></item>
		<item>
		<title>Silverlight 4 Webcam and Microphone Support</title>
		<link>http://feedproxy.google.com/~r/ryanmwright/bSlq/~3/z9S6lg_J6lM/</link>
		<comments>http://www.ryanmwright.com/2009/12/14/silverlight-4-webcam-and-microphone-support/#comments</comments>
		<pubDate>Mon, 14 Dec 2009 19:10:51 +0000</pubDate>
		<dc:creator>Ryan</dc:creator>
				<category><![CDATA[Silverlight]]></category>
		<category><![CDATA[linkedin]]></category>

		<guid isPermaLink="false">http://www.ryanmwright.com/?p=128</guid>
		<description><![CDATA[The beta of Silverlight 4 has lots of neat stuff packaged with it. One of these things included is native Webcam and Microphone support. In this post I will show how to create a simple webcam viewer complete with A/V &#8230;<p class="read-more"><a href="http://www.ryanmwright.com/2009/12/14/silverlight-4-webcam-and-microphone-support/">Read more &#187;</a></p>]]></description>
				<content:encoded><![CDATA[<p>The beta of Silverlight 4 has lots of neat stuff packaged with it. One of these things included is native Webcam and Microphone support. In this post I will show how to create a simple webcam viewer complete with A/V and picture capture. However, one thing not currently in the beta is streaming the video and audio to a remote destination such as a file, another client, or a media server. I have not tried to implement this in this functionality in this post because it will most likely be implemented by the time the final release comes out, so rather than &#8220;pre-invent&#8221; the wheel, I have decided to wait until Microsoft releases a version of their own. I have, however, included implementations of the AudioSink and VideoSink which can be used to pull the raw audio and video and either stream it or save it. Since the the data passed to the OnSamples () method in VideoSink is nothing more than 32bpp ARGB raw bitmap frames it is feasible to create a custom implementation that can save the video to a stream suitable for saving to a file or media server. Be sure to check out the bottom of the post for links to a screenshot of the final result and complete source code. There are also links to shader effects and an attempt at saving the streams to AVI files.</p>
<p>First, to house the video we create a rectangle in XAML. Note the render transform. This will flip the image about it&#8217;s Y-Axis so it appears like a mirror, which is more intuitive when viewing the video on screen.</p>
<pre class="brush: xml; title: ; notranslate">
        &lt;Rectangle Height=&quot;254&quot; HorizontalAlignment=&quot;Left&quot; Margin=&quot;31,47,0,0&quot; Name=&quot;VideoPane&quot; Stroke=&quot;Black&quot; StrokeThickness=&quot;1&quot; VerticalAlignment=&quot;Top&quot; Width=&quot;511&quot; RenderTransformOrigin=&quot;0.5,0.5&quot;&gt;
            &lt;Rectangle.RenderTransform&gt;
                &lt;TransformGroup&gt;
                    &lt;ScaleTransform ScaleX=&quot;-1&quot;/&gt;
                    &lt;SkewTransform AngleX=&quot;0&quot; AngleY=&quot;0&quot;/&gt;
                    &lt;RotateTransform Angle=&quot;0&quot;/&gt;
                    &lt;TranslateTransform/&gt;
                &lt;/TransformGroup&gt;
            &lt;/Rectangle.RenderTransform&gt;
        &lt;/Rectangle&gt;
</pre>
<p>Next we create two listboxes to show a list of devices suitable for audio and video capture. The user needs to select an audio and video device before starting capture.</p>
<pre class="brush: xml; title: ; notranslate">
        &lt;dataInput:Label Height=&quot;18&quot; HorizontalAlignment=&quot;Left&quot; Margin=&quot;109,426,0,0&quot; Content=&quot;Video Devices&quot; Name=&quot;label1&quot; VerticalAlignment=&quot;Top&quot; Width=&quot;83&quot; /&gt;
        &lt;ListBox Height=&quot;48&quot; Margin=&quot;31,443,0,0&quot; Name=&quot;VideoCaptureDevices&quot; VerticalAlignment=&quot;Top&quot; HorizontalAlignment=&quot;Left&quot; Width=&quot;256&quot;&gt;
            &lt;ListBox.ItemTemplate&gt;
                &lt;DataTemplate&gt;
                    &lt;TextBlock Text=&quot;{Binding FriendlyName}&quot; /&gt;
                &lt;/DataTemplate&gt;
            &lt;/ListBox.ItemTemplate&gt;
        &lt;/ListBox&gt;
        &lt;dataInput:Label Height=&quot;18&quot; HorizontalAlignment=&quot;Left&quot; Margin=&quot;367,426,0,0&quot; Content=&quot;Audio Devices&quot; Name=&quot;label2&quot; VerticalAlignment=&quot;Top&quot; Width=&quot;83&quot; /&gt;
        &lt;ListBox Height=&quot;48&quot; HorizontalAlignment=&quot;Left&quot; Margin=&quot;293,443,0,0&quot; Name=&quot;AudioCaptureDevices&quot; VerticalAlignment=&quot;Top&quot; Width=&quot;249&quot;&gt;
            &lt;ListBox.ItemTemplate&gt;
                &lt;DataTemplate&gt;
                    &lt;TextBlock Text=&quot;{Binding FriendlyName}&quot; /&gt;
                &lt;/DataTemplate&gt;
            &lt;/ListBox.ItemTemplate&gt;
        &lt;/ListBox&gt; 
</pre>
<p>The following will display a list of images captured when the user takes a CamShot:</p>
<pre class="brush: xml; title: ; notranslate">
        &lt;ScrollViewer Margin=&quot;34,313,33,102&quot; Width=&quot;505&quot; VerticalScrollBarVisibility=&quot;Hidden&quot; HorizontalScrollBarVisibility=&quot;Auto&quot;&gt;
            &lt;ItemsControl x:Name=&quot;PhotoShots&quot; Height=&quot;106&quot;&gt;
                &lt;ItemsControl.ItemTemplate&gt;
                    &lt;DataTemplate&gt;
                        &lt;Image Source=&quot;{Binding}&quot; Margin=&quot;5&quot; Stretch=&quot;UniformToFill&quot; Height=&quot;70&quot; /&gt;
                    &lt;/DataTemplate&gt;
                &lt;/ItemsControl.ItemTemplate&gt;
                &lt;ItemsControl.ItemsPanel&gt;
                    &lt;ItemsPanelTemplate&gt;
                        &lt;StackPanel Orientation=&quot;Horizontal&quot; VerticalAlignment=&quot;Center&quot; HorizontalAlignment=&quot;Center&quot; /&gt;
                    &lt;/ItemsPanelTemplate&gt;
                &lt;/ItemsControl.ItemsPanel&gt;
            &lt;/ItemsControl&gt;
        &lt;/ScrollViewer&gt;
</pre>
<p>In our code behind we create a capture source and an ObservableCollection to hold our images captured from the webcam. We then get the list of devices and set the item source properties when the page is loaded.</p>
<pre class="brush: csharp; title: ; notranslate">
        CaptureSource captureSource = new CaptureSource();
        ObservableCollection&lt;WriteableBitmap&gt; photoShots = new ObservableCollection&lt;WriteableBitmap&gt;();

        public MainPage()
        {
            InitializeComponent();
            this.Loaded += new RoutedEventHandler(MainPage_Loaded);
        }

        void MainPage_Loaded(object sender, RoutedEventArgs e)
        {
            VideoCaptureDevices.ItemsSource = CaptureDeviceConfiguration.GetAvailableVideoCaptureDevices();
            AudioCaptureDevices.ItemsSource = CaptureDeviceConfiguration.GetAvailableAudioCaptureDevices();
            PhotoShots.ItemsSource = photoShots;
        }
</pre>
<p>When we are told to start the capture, make sure the user selected the audio and video capture devices, request access to the device, and if the user allowed us access then set the sink capture sources, create the brush, start capture, and give the rectangle a drop shadow effect.</p>
<pre class="brush: csharp; title: ; notranslate">
        private void StartWebCam_Click(object sender, RoutedEventArgs e)
        {
            if (VideoCaptureDevices.SelectedItem != null &amp;&amp; AudioCaptureDevices.SelectedItem != null)
            {
                //stop the source if it's started
                if (captureSource.State == CaptureState.Started)
                {
                    captureSource.Stop();
                }

                //VideoCaptureDevice device = CaptureDeviceConfiguration.GetDefaultVideoCaptureDevice();
                VideoCaptureDevice videoDevice = VideoCaptureDevices.SelectedItem as VideoCaptureDevice;

                //AudioCaptureDevice device = CaptureDeviceConfiguration.GetDefaultAudioCaptureDevice();
                AudioCaptureDevice audioDevice = AudioCaptureDevices.SelectedItem as AudioCaptureDevice;

                //check to see if we have already been allowed access or request if we have not
                if (CaptureDeviceConfiguration.AllowedDeviceAccess || CaptureDeviceConfiguration.RequestDeviceAccess())
                {
                    captureSource.VideoCaptureDevice = videoDevice;
                    captureSource.AudioCaptureDevice = audioDevice;

                    //set the video sink's capture source
                    videoSink.CaptureSource = captureSource;
                    audioSink.CaptureSource = captureSource;

                    //create a brush and set the source to the capture source
                    VideoBrush brush = new VideoBrush();
                    brush.SetSource(captureSource);
                    brush.Stretch = Stretch.UniformToFill;

                    //start the capture
                    captureSource.Start();

                    //paint the rectangle with the video
                    VideoPane.Fill = brush;

                    //set the effect
                    VideoPane.Effect = new DropShadowEffect() { ShadowDepth = 7 };
                }
            }
            else
            {
                MessageBox.Show(&quot;Please select an audio and a video capture device to start the session&quot;);
            }
        }
</pre>
<p>The following will take a screenshot and add it to the observable collection of WriteableBitmaps:</p>
<pre class="brush: csharp; title: ; notranslate">
        private void TakeCamShot_Click(object sender, RoutedEventArgs e)
        {
            if (captureSource != null)
            {
                captureSource.AsyncCaptureImage(
                    (img) =&gt;
                    {
                        photoShots.Add(img);
                    });
            }
        }
</pre>
<p>Below are the AudioSink and VideoSink implementations. They can be extended to save the streams to whatever destination is needed:</p>
<pre class="brush: csharp; title: ; notranslate">
    /// &lt;summary&gt;
    /// 
    /// &lt;/summary&gt;
    public class MSVideoSink : VideoSink
    {
        /// &lt;summary&gt;
        /// Video
        /// &lt;/summary&gt;
        public MemoryStream VideoStream
        {
            get;
            private set;
        }


        /// &lt;summary&gt;
        /// Format
        /// &lt;/summary&gt;
        public VideoFormat Format
        {
            get;
            set;
        }

        /// &lt;summary&gt;
        /// Capture started
        /// &lt;/summary&gt;
        protected override void OnCaptureStarted()
        {
            VideoStream = new MemoryStream();
        }


        /// &lt;summary&gt;
        /// Capture stops
        /// &lt;/summary&gt;
        protected override void OnCaptureStopped()
        {
            //post-process video data
        }


        /// &lt;summary&gt;
        /// Format changing
        /// &lt;/summary&gt;
        /// &lt;param name=&quot;videoFormat&quot;&gt;&lt;/param&gt;
        protected override void OnFormatChange(VideoFormat videoFormat)
        {
            //do stuff on format change, i.e. close off stream, etc
            Format = videoFormat;
        }

        /// &lt;summary&gt;
        /// Write the sample data to the stream
        /// &lt;/summary&gt;
        /// &lt;param name=&quot;sampleTime&quot;&gt;&lt;/param&gt;
        /// &lt;param name=&quot;frameDuration&quot;&gt;&lt;/param&gt;
        /// &lt;param name=&quot;sampleData&quot;&gt;&lt;/param&gt;
        protected override void OnSample(long sampleTime, long frameDuration, byte[] sampleData)
        {
            //do something here...i.e. stream it to a file, etc
            //VideoStream.Write(sampleData, 0, sampleData.Length);
        }
    }


    /// &lt;summary&gt;
    /// 
    /// &lt;/summary&gt;
    public class MSAudioSink : AudioSink
    {
        /// &lt;summary&gt;
        /// Capture starts
        /// &lt;/summary&gt;
        protected override void OnCaptureStarted()
        {
            WaveformDataStream = new MemoryStream();
        }

        /// &lt;summary&gt;
        /// Post-process audio data
        /// &lt;/summary&gt;
        protected override void OnCaptureStopped()
        {
        }

        /// &lt;summary&gt;
        /// Audio format
        /// &lt;/summary&gt;
        public AudioFormat Format
        {
            get;
            set;
        }

        /// &lt;summary&gt;
        /// Memory stream
        /// &lt;/summary&gt;
        public MemoryStream WaveformDataStream
        {
            get;
            set;
        }

        /// &lt;summary&gt;
        /// Format is changing
        /// &lt;/summary&gt;
        /// &lt;param name=&quot;format&quot;&gt;&lt;/param&gt;
        protected override void OnFormatChange(AudioFormat format)
        {
            //do stuff on format change, i.e. close off stream, etc
            Format = format;
        }

        /// &lt;summary&gt;
        /// Save each sample
        /// &lt;/summary&gt;
        /// &lt;param name=&quot;sampleTime&quot;&gt;&lt;/param&gt;
        /// &lt;param name=&quot;sampleDuration&quot;&gt;&lt;/param&gt;
        /// &lt;param name=&quot;sampleData&quot;&gt;&lt;/param&gt;
        protected override void OnSamples(long sampleTime, long sampleDuration, byte[] sample)
        {
            //do something here...i.e. stream it to a file, etc
           // WaveformDataStream.Write(sample, 0, sample.Length);
        }
    }  
</pre>
<p>Below is a screenshot of the result:<br />
<a href="http://content.ryanmwright.com/2009/12/WebcamAppScreenshot.png"><img src="http://www.ryanmwright.com/wp-content/plugins/jquery-image-lazy-loading/images/grey.gif" data-original="http://content.ryanmwright.com/2009/12/WebcamAppScreenshot-150x150.png" alt="WebcamAppScreenshot" title="WebcamAppScreenshot" width="150" height="150" class="lazy alignnone size-thumbnail wp-image-140" /><noscript><img src="http://content.ryanmwright.com/2009/12/WebcamAppScreenshot-150x150.png" alt="WebcamAppScreenshot" title="WebcamAppScreenshot" width="150" height="150" class="alignnone size-thumbnail wp-image-140" /></noscript></a></p>
<p>Download the source code (VS2010):<br />
<a href='http://content.ryanmwright.com/2009/12/SilverlightWebcamApp.zip'>SilverlightWebcamApp.zip</a></p>
<p>Edge detection (some cool effects):<br />
<a href="http://kodierer.blogspot.com/2009/11/edgecam-silverlight-4-webcam-edge.html">http://kodierer.blogspot.com/2009/11/edgecam-silverlight-4-webcam-edge.html</a><br />
<a href="http://kodierer.blogspot.com/2009/07/livin-on-edge-silverlight-parametric_4324.html">http://kodierer.blogspot.com/2009/07/livin-on-edge-silverlight-parametric_4324.html</a></p>
<p>Saving streams to AVI files:<br />
<a href="http://mtaulty.com/CommunityServer/blogs/mike_taultys_blog/archive/2009/12/10/silverlight-4-more-on-capturing-video-from-webcams.aspx">http://mtaulty.com/CommunityServer/blogs/mike_taultys_blog/archive/2009/12/10/silverlight-4-more-on-capturing-video-from-webcams.aspx</a></p>
<script type="text/javascript">(function() {var s = document.createElement('SCRIPT'), s1 = document.getElementsByTagName('SCRIPT')[0];s.type = 'text/javascript';s.async = true;s.src = 'http://widgets.digg.com/buttons.js';s1.parentNode.insertBefore(s, s1);})();</script><a class="DiggThisButton DiggCompact" href="http://digg.com/submit?url=http%3A%2F%2Fwww.ryanmwright.com%2F2009%2F12%2F14%2Fsilverlight-4-webcam-and-microphone-support%2F"></a><img src="http://feeds.feedburner.com/~r/ryanmwright/bSlq/~4/z9S6lg_J6lM" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.ryanmwright.com/2009/12/14/silverlight-4-webcam-and-microphone-support/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		<feedburner:origLink>http://www.ryanmwright.com/2009/12/14/silverlight-4-webcam-and-microphone-support/</feedburner:origLink></item>
		<item>
		<title>CableCards For All! Home Theatre PC Heaven.</title>
		<link>http://feedproxy.google.com/~r/ryanmwright/bSlq/~3/RXq4eTSxelc/</link>
		<comments>http://www.ryanmwright.com/2009/10/19/cablecards-for-all-home-theatre-pc-heaven/#comments</comments>
		<pubDate>Mon, 19 Oct 2009 04:11:06 +0000</pubDate>
		<dc:creator>Ryan</dc:creator>
				<category><![CDATA[HTPC]]></category>
		<category><![CDATA[CableCard]]></category>
		<category><![CDATA[Home Theatre]]></category>
		<category><![CDATA[Media Center]]></category>
		<category><![CDATA[TV]]></category>

		<guid isPermaLink="false">http://www.ryanmwright.com/?p=82</guid>
		<description><![CDATA[It&#8217;s finally here! CableLabs finally loosened their reigns and is allowing custom built home theatre PC&#8217;s to get in on the digital cable goodness. I&#8217;ve got my eye on the Ceton Multi-Channel Cable TV Card which allows up to 6 &#8230;<p class="read-more"><a href="http://www.ryanmwright.com/2009/10/19/cablecards-for-all-home-theatre-pc-heaven/">Read more &#187;</a></p>]]></description>
				<content:encoded><![CDATA[<p>It&#8217;s finally here! CableLabs finally loosened their reigns and is allowing custom built home theatre PC&#8217;s to get in on the digital cable goodness. I&#8217;ve got my eye on the <a href="http://www.cetoncorp.com/ProductsVista.php" target="_blank">Ceton Multi-Channel Cable TV Card</a> which allows up to 6 digital cable streams! The only problem is that it&#8217;s projected release date is Q1 of 2010, which won&#8217;t come soon enough. <a href="http://www.gearlog.com/2009/09/hauppauge_to_sell_cablecard_tu.php" target="_blank">Hauppauge is also looking to get one to market</a> a little earlier, by end of 2009. This one looks like a single tuner only, but for those who just can&#8217;t wait it seems like it would be a great buy. It&#8217;s also good to note that these are alleged to only work with Windows 7, so you Vista users have another reason to upgrade (as if you needed one!)</p>
<p>For those of you looking to build an HTPC for yourself I posted my specs below. The final price tag came to around $1,100, but it was well worth it considering I use it every day. Note that I bought these parts almost a year ago, so the price for the some of the components has most likely went down some. The PC is currently used by 2 extenders, and I also use this machine as a PlayOn server so the PS3 in my house can get on demand content as well.</p>
<ul>
<li>
ZALMAN CNPS9500 AT 2 Ball CPU Cooling Fan/Heatsink &#8211; Retail</li>
<li>
ZALMAN ZM &#8211; NBF 47 Aluminum Heatsinks only &#8211; Retail  </li>
<li>
CORSAIR 4GB (2 x 2GB) 240-Pin DDR2 SDRAM DDR2 800 (PC2 6400) Dual Channel Kit Desktop Memory Model TWIN2X4096-6400C5 &#8211; Retail</li>
<li>
LG Black LG Blu-ray/HD DVD-ROM &amp; 16X DVD±R DVD Burner SATA Model GGC-H20L &#8211; OEM</li>
<li>
[2x] &#8211; Western Digital Caviar Black WD1001FALS 1TB 7200 RPM SATA 3.0Gb/s 3.5&#8243; Internal Hard Drives -Bare Drive</li>
<li>
Hauppauge WinTV-HVR-2250 Dual TV Tuner / Encoder 1229 PCI-Express x1 Interface &#8211; Retail</li>
<li>
RAIDMAX HYBRID 2 RX-630SS 630W ATX12V V2.2/ EPS12V SLI Ready CrossFire Ready Modular Modular LED Power Supply &#8211; Retail</li>
<li>
GIGABYTE GA-E7AUM-DS2H LGA 775 NVIDIA GeForce 9400 HDMI Micro ATX Intel Motherboard &#8211; Retail</li>
<li>
Intel Core 2 Quad Q6600 Kentsfield 2.4GHz LGA 775 Quad-Core Processor Model BX80562Q6600 &#8211; Retail</li>
<li>
Zalman Tech HD160PLUS Zalman HTC HD160 Plus Home Theatre PC Enclosure (Black)</li>
<li>
Windows 7</li>
<li>
Boxee &#8211; An awesome (free) application that allows you to stream sources (some in HighDef!) like NetFlix, YouTube, Pandora, last.fm, ShoutCast, ComedyCentral, AppleTrailers, Joost, Flickr, MTV and Hulu just to name a few. <a href="http://mymce.wordpress.com/2009/01/28/how-to-integrate-boxee-for-windows-into-vista-media-center/" target="_blank">With 10 minutes worth of work</a> it&#8217;s able to be launched completely from MediaCenter.</li>
<li>
ArcSoft TotalMedia Theatre 3 Platinum &#8211; For Blu-Ray playback</li>
</ul>
<p>One thing I would like to do as well is setup X10 Home Automation software for the media center. I&#8217;ll post on it later if I get it setup.</p>
<table border="0">
<tbody>
<tr>
<td>
<div id="attachment_53" class="wp-caption alignnone" style="width: 160px"><a href="http://content.ryanmwright.com/2009/10/015.jpg"><img class="lazy size-thumbnail wp-image-53" title="015" src="http://www.ryanmwright.com/wp-content/plugins/jquery-image-lazy-loading/images/grey.gif" data-original="http://content.ryanmwright.com/2009/10/015-150x150.jpg" alt="Media Center Machine" width="150" height="150" /><noscript><img class="size-thumbnail wp-image-53" title="015" src="http://content.ryanmwright.com/2009/10/015-150x150.jpg" alt="Media Center Machine" width="150" height="150" /></noscript></a><p class="wp-caption-text">Media Center Machine</p></div>
</td>
<td>
<div id="attachment_67" class="wp-caption alignnone" style="width: 160px"><a href="http://content.ryanmwright.com/2009/10/029.jpg"><img class="lazy size-thumbnail wp-image-67" title="029" src="http://www.ryanmwright.com/wp-content/plugins/jquery-image-lazy-loading/images/grey.gif" data-original="http://content.ryanmwright.com/2009/10/029-150x150.jpg" alt="Remote" width="150" height="150" /><noscript><img class="size-thumbnail wp-image-67" title="029" src="http://content.ryanmwright.com/2009/10/029-150x150.jpg" alt="Remote" width="150" height="150" /></noscript></a><p class="wp-caption-text">Remote</p></div>
</td>
<td>
<div id="attachment_69" class="wp-caption alignnone" style="width: 160px"><a href="http://content.ryanmwright.com/2009/10/031.jpg"><img class="lazy size-thumbnail wp-image-69" title="031" src="http://www.ryanmwright.com/wp-content/plugins/jquery-image-lazy-loading/images/grey.gif" data-original="http://content.ryanmwright.com/2009/10/031-150x150.jpg" alt="Media Center Available Space" width="150" height="150" /><noscript><img class="size-thumbnail wp-image-69" title="031" src="http://content.ryanmwright.com/2009/10/031-150x150.jpg" alt="Media Center Available Space" width="150" height="150" /></noscript></a><p class="wp-caption-text">Media Center Available Space</p></div>
</td>
</tr>
<tr>
<td>
<div id="attachment_68" class="wp-caption alignnone" style="width: 160px"><a href="http://content.ryanmwright.com/2009/10/030.jpg"><img class="lazy size-thumbnail wp-image-68" title="030" src="http://www.ryanmwright.com/wp-content/plugins/jquery-image-lazy-loading/images/grey.gif" data-original="http://content.ryanmwright.com/2009/10/030-150x150.jpg" alt="FM Radio" width="150" height="150" /><noscript><img class="size-thumbnail wp-image-68" title="030" src="http://content.ryanmwright.com/2009/10/030-150x150.jpg" alt="FM Radio" width="150" height="150" /></noscript></a><p class="wp-caption-text">FM Radio</p></div>
</td>
<td>
<div id="attachment_54" class="wp-caption alignnone" style="width: 160px"><a href="http://content.ryanmwright.com/2009/10/016.jpg"><img class="lazy size-thumbnail wp-image-54" title="016" src="http://www.ryanmwright.com/wp-content/plugins/jquery-image-lazy-loading/images/grey.gif" data-original="http://content.ryanmwright.com/2009/10/016-150x150.jpg" alt="Fringe" width="150" height="150" /><noscript><img class="size-thumbnail wp-image-54" title="016" src="http://content.ryanmwright.com/2009/10/016-150x150.jpg" alt="Fringe" width="150" height="150" /></noscript></a><p class="wp-caption-text">Fringe</p></div>
</td>
<td>
<div id="attachment_59" class="wp-caption alignnone" style="width: 160px"><a href="http://content.ryanmwright.com/2009/10/021.jpg"><img class="lazy size-thumbnail wp-image-59" title="021" src="http://www.ryanmwright.com/wp-content/plugins/jquery-image-lazy-loading/images/grey.gif" data-original="http://content.ryanmwright.com/2009/10/021-150x150.jpg" alt="NFL Guide" width="150" height="150" /><noscript><img class="size-thumbnail wp-image-59" title="021" src="http://content.ryanmwright.com/2009/10/021-150x150.jpg" alt="NFL Guide" width="150" height="150" /></noscript></a><p class="wp-caption-text">NFL Guide</p></div>
</td>
</tr>
<tr>
<td>
<div id="attachment_58" class="wp-caption alignnone" style="width: 160px"><a href="http://content.ryanmwright.com/2009/10/020.jpg"><img class="lazy size-thumbnail wp-image-58" title="020" src="http://www.ryanmwright.com/wp-content/plugins/jquery-image-lazy-loading/images/grey.gif" data-original="http://content.ryanmwright.com/2009/10/020-150x150.jpg" alt="NFL Guide 2" width="150" height="150" /><noscript><img class="size-thumbnail wp-image-58" title="020" src="http://content.ryanmwright.com/2009/10/020-150x150.jpg" alt="NFL Guide 2" width="150" height="150" /></noscript></a><p class="wp-caption-text">NFL Guide 2</p></div>
</td>
<td>
<div id="attachment_57" class="wp-caption alignnone" style="width: 160px"><a href="http://content.ryanmwright.com/2009/10/019.jpg"><img class="lazy size-thumbnail wp-image-57" title="019" src="http://www.ryanmwright.com/wp-content/plugins/jquery-image-lazy-loading/images/grey.gif" data-original="http://content.ryanmwright.com/2009/10/019-150x150.jpg" alt="Iron Man on Blu-Ray" width="150" height="150" /><noscript><img class="size-thumbnail wp-image-57" title="019" src="http://content.ryanmwright.com/2009/10/019-150x150.jpg" alt="Iron Man on Blu-Ray" width="150" height="150" /></noscript></a><p class="wp-caption-text">Iron Man on Blu-Ray</p></div>
</td>
<td>
<div id="attachment_60" class="wp-caption alignnone" style="width: 160px"><a href="http://content.ryanmwright.com/2009/10/022.jpg"><img class="lazy size-thumbnail wp-image-60" title="022" src="http://www.ryanmwright.com/wp-content/plugins/jquery-image-lazy-loading/images/grey.gif" data-original="http://content.ryanmwright.com/2009/10/022-150x150.jpg" alt="Boxee Link in Media Center" width="150" height="150" /><noscript><img class="size-thumbnail wp-image-60" title="022" src="http://content.ryanmwright.com/2009/10/022-150x150.jpg" alt="Boxee Link in Media Center" width="150" height="150" /></noscript></a><p class="wp-caption-text">Boxee Link in Media Center</p></div>
</td>
</tr>
<tr>
<td>
<div id="attachment_66" class="wp-caption alignnone" style="width: 160px"><a href="http://content.ryanmwright.com/2009/10/028.jpg"><img class="lazy size-thumbnail wp-image-66" title="028" src="http://www.ryanmwright.com/wp-content/plugins/jquery-image-lazy-loading/images/grey.gif" data-original="http://content.ryanmwright.com/2009/10/028-150x150.jpg" alt="Boxee" width="150" height="150" /><noscript><img class="size-thumbnail wp-image-66" title="028" src="http://content.ryanmwright.com/2009/10/028-150x150.jpg" alt="Boxee" width="150" height="150" /></noscript></a><p class="wp-caption-text">Boxee</p></div>
</td>
<td>
<div id="attachment_62" class="wp-caption alignnone" style="width: 160px"><a href="http://content.ryanmwright.com/2009/10/024.jpg"><img class="lazy size-thumbnail wp-image-62" title="024" src="http://www.ryanmwright.com/wp-content/plugins/jquery-image-lazy-loading/images/grey.gif" data-original="http://content.ryanmwright.com/2009/10/024-150x150.jpg" alt="Boxee Applications" width="150" height="150" /><noscript><img class="size-thumbnail wp-image-62" title="024" src="http://content.ryanmwright.com/2009/10/024-150x150.jpg" alt="Boxee Applications" width="150" height="150" /></noscript></a><p class="wp-caption-text">Boxee Applications</p></div>
</td>
<td>
<div id="attachment_65" class="wp-caption alignnone" style="width: 160px"><a href="http://content.ryanmwright.com/2009/10/027.jpg"><img class="lazy size-thumbnail wp-image-65" title="027" src="http://www.ryanmwright.com/wp-content/plugins/jquery-image-lazy-loading/images/grey.gif" data-original="http://content.ryanmwright.com/2009/10/027-150x150.jpg" alt="Pandora in Boxee" width="150" height="150" /><noscript><img class="size-thumbnail wp-image-65" title="027" src="http://content.ryanmwright.com/2009/10/027-150x150.jpg" alt="Pandora in Boxee" width="150" height="150" /></noscript></a><p class="wp-caption-text">Pandora in Boxee</p></div>
</td>
</tr>
<tr>
<td>
<div id="attachment_64" class="wp-caption alignnone" style="width: 160px"><a href="http://content.ryanmwright.com/2009/10/026.jpg"><img class="lazy size-thumbnail wp-image-64" title="026" src="http://www.ryanmwright.com/wp-content/plugins/jquery-image-lazy-loading/images/grey.gif" data-original="http://content.ryanmwright.com/2009/10/026-150x150.jpg" alt="Pandora in Boxee 2" width="150" height="150" /><noscript><img class="size-thumbnail wp-image-64" title="026" src="http://content.ryanmwright.com/2009/10/026-150x150.jpg" alt="Pandora in Boxee 2" width="150" height="150" /></noscript></a><p class="wp-caption-text">Pandora in Boxee 2</p></div>
</td>
<td>
<div id="attachment_63" class="wp-caption alignnone" style="width: 160px"><a href="http://content.ryanmwright.com/2009/10/025.jpg"><img class="lazy size-thumbnail wp-image-63" title="025" src="http://www.ryanmwright.com/wp-content/plugins/jquery-image-lazy-loading/images/grey.gif" data-original="http://content.ryanmwright.com/2009/10/025-150x150.jpg" alt="Pandora Boxee Visualization" width="150" height="150" /><noscript><img class="size-thumbnail wp-image-63" title="025" src="http://content.ryanmwright.com/2009/10/025-150x150.jpg" alt="Pandora Boxee Visualization" width="150" height="150" /></noscript></a><p class="wp-caption-text">Pandora Boxee Visualization</p></div>
</td>
<td>
<div id="attachment_61" class="wp-caption alignnone" style="width: 160px"><a href="http://content.ryanmwright.com/2009/10/023.jpg"><img class="lazy size-thumbnail wp-image-61" title="023" src="http://www.ryanmwright.com/wp-content/plugins/jquery-image-lazy-loading/images/grey.gif" data-original="http://content.ryanmwright.com/2009/10/023-150x150.jpg" alt="Pandora Boxee Visualization 2" width="150" height="150" /><noscript><img class="size-thumbnail wp-image-61" title="023" src="http://content.ryanmwright.com/2009/10/023-150x150.jpg" alt="Pandora Boxee Visualization 2" width="150" height="150" /></noscript></a><p class="wp-caption-text">Pandora Boxee Visualization 2</p></div>
</td>
</tr>
</tbody>
</table>
<script type="text/javascript">(function() {var s = document.createElement('SCRIPT'), s1 = document.getElementsByTagName('SCRIPT')[0];s.type = 'text/javascript';s.async = true;s.src = 'http://widgets.digg.com/buttons.js';s1.parentNode.insertBefore(s, s1);})();</script><a class="DiggThisButton DiggCompact" href="http://digg.com/submit?url=http%3A%2F%2Fwww.ryanmwright.com%2F2009%2F10%2F19%2Fcablecards-for-all-home-theatre-pc-heaven%2F"></a><img src="http://feeds.feedburner.com/~r/ryanmwright/bSlq/~4/RXq4eTSxelc" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.ryanmwright.com/2009/10/19/cablecards-for-all-home-theatre-pc-heaven/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.ryanmwright.com/2009/10/19/cablecards-for-all-home-theatre-pc-heaven/</feedburner:origLink></item>
	</channel>
</rss><!-- Performance optimized by W3 Total Cache. Learn more: http://www.w3-edge.com/wordpress-plugins/

Page Caching using n/a
Database Caching 8/14 queries in 0.017 seconds using disk: basic
Object Caching 1214/1384 objects using disk: basic

 Served from: www.ryanmwright.com @ 2013-06-19 13:57:34 by W3 Total Cache -->
