<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	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/"
	>

<channel>
	<title>3DN Technology</title>
	<atom:link href="http://wordpress.3dn.nl/feed/" rel="self" type="application/rss+xml" />
	<link>http://wordpress.3dn.nl</link>
	<description>3DN Technology Blog</description>
	<lastBuildDate>Wed, 27 Jul 2011 07:14:18 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Google+ Profiles for Google Apps</title>
		<link>http://wordpress.3dn.nl/2011/07/08/google-profiles-for-google-apps/</link>
		<comments>http://wordpress.3dn.nl/2011/07/08/google-profiles-for-google-apps/#comments</comments>
		<pubDate>Fri, 08 Jul 2011 10:00:29 +0000</pubDate>
		<dc:creator>Fred Leeflang</dc:creator>
				<category><![CDATA[internet]]></category>
		<category><![CDATA[social]]></category>
		<category><![CDATA[google]]></category>
		<category><![CDATA[groups]]></category>

		<guid isPermaLink="false">http://wordpress.3dn.nl/?p=2247</guid>
		<description><![CDATA[Google Profile for Google Apps?]]></description>
			<content:encoded><![CDATA[<p><strong>UPDATE Jul 14th: </strong>Google has just posted <a title="Google+ Entity Profile Application" href="https://docs.google.com/spreadsheet/viewform?hl=en_US&amp;formkey=dGY5QUU2SHR3X21NeERpWGRNcDNZTVE6MQ&amp;ndplr=1#gid=0" target="_blank">a form</a> where business users can sign up for Google+</p>
<blockquote><p>&#8220;Fill out this form to apply for consideration in our program allowing a limited number of businesses to use Google+. We will be using this small experiment to see the effect of including brands in the Google+ experience.&#8221;</p></blockquote>
<p>Having been an early fan of Google, naturally I tried to figure out if I could join Google+ in the beta. It wasn&#8217;t very difficult to do and when I did I happened to be logged in with my fredl@dutchie.org Google Profile and all was well.</p>
<p>So being busy with this, I decided to check out some other pains from the past (mind you, I specifically said &#8216;my fredl@dutchie.org Google Profile, meaning that this was NOT my Google Apps fredl@dutchie.org account; I found out I could create an @dutchie.org Google Profile <strong>next to</strong> my @dutchie.org Google Apps account years ago, unfortunately quite a while after already having created a fred.leeflang@gmail.com profile *sigh*)</p>
<p>So I logged in to my fred.leeflang@gmail.com profile and checked out my Picasa. Well wow, FINALLY Google had created a simple way to migrate my Picasa albums to my other account, the fredl@dutchie.org Google Profile. Whoohoo, that meant&#8230; could it be true&#8230; that I could share my Picasa pictures on Google+ now! Testing this confirmed it and I was happy as a pig in snot. Encouraged by this success, I noticed the window that said my fredl@dutchie.org Google Profile would be forcefully moved under the management of my @dutchie.org Google Apps administrator, somewhere in July 16th if I remember correctly. So I decided what the heck and moved stuff aheas of time.</p>
<p>BIG MISTAKE! Trying to log back in on Google+ after the migration, I suddenly get this message saying Google Profiles is not supported under Google Apps. I had to login on Google using my (now) incomplete temp account.</p>
<p>So then I went to &#8212; google &#8212; for any news on this subject and it turns out Google has announced in March that it would be a few weeks before Google Profiles would be available on Google Apps. In May they announced it would be &#8216;a few more months&#8217;. So much for Google promises, I hope their promises on privacy will be more reliable.</p>
<p>But then this morning something interesting happened. I was reading a <a title="Forum" href="https://groups.google.com/a/googleproductforums.com/forum/#%21msg/google-plus-discuss/o9Tn_H85gkg/ShaSO_tXhHcJ" target="_blank">Google group</a> and decided I&#8217;d comment on a post. For that, you have to join the group first. And what do you know..</p>
<p><a href="http://wordpress.3dn.nl/files/2011/07/Screenshot-1.png"><img class="size-full wp-image-2248 alignnone" title="Screenshot-1" src="http://wordpress.3dn.nl/files/2011/07/Screenshot-1.png" alt="" width="612" height="464" /></a></p>
<p>Of course, this is in Dutch, but it says there under &#8216;Google-profiel&#8217; &#8216;Bied een link naar mijn dutchie.org-profiel&#8217; (give a link to my dutchie.org profile). The only thing I see different now than the first time I tried this is that my profile picture is now not my dutchie.org profile picture (which I think it was the first time I tried this).</p>
<p><strong>Update:</strong></p>
<p>Well, I just checked the &#8216;Bied een link naar mijn dutchie.org-profiel&#8217; which did make my Google Apps profile picture show up.</p>
<p><a href="http://wordpress.3dn.nl/files/2011/07/Screenshot-2.png"><img class="alignnone size-full wp-image-2251" title="Screenshot-2" src="http://wordpress.3dn.nl/files/2011/07/Screenshot-2.png" alt="" width="613" height="383" /></a></p>
<p>Mind you, it also shows a link to my profile!! Clicking on the link however results in a message saying:</p>
<h3>This service is not available</h3>
<p>Profiles is not available for dutchie.org. <a href="http://www.google.com/support/accounts/bin/answer.py?answer=182213&amp;hl=en&amp;ctx=ch_b%2F0%2FServiceNotAllowed&amp;p=profiles" target="_blank">Learn more</a> about Google products you can use with fredl@dutchie.org.</p>
<p>If you are the Google Apps administrator, please read these articles to learn more about <a href="http://www.google.com/support/a/bin/answer.py?answer=182426" target="_blank">controlling user access to Google Apps services</a> and <a href="http://www.google.com/support/a/bin/answer.py?answer=182442" target="_blank">turning services on/off for certain users</a>.</p>
<p>Did you use this product with a different Google Account? <a href="https://www.google.com/accounts/Logout?continue=http%3A%2F%2Fwww.google.com%2Fprofiles%2Fu%2F0%2Fme%2Feditprofile&amp;il=true&amp;zx=1v6eufejfppnu">Sign out</a> of your current Google Account and then sign in to the account you want.</p>
Note: There is a poll embedded within this post, please visit the site to participate in this post's poll.
]]></content:encoded>
			<wfw:commentRss>http://wordpress.3dn.nl/2011/07/08/google-profiles-for-google-apps/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Scooba-Doo Underwater Scooter</title>
		<link>http://wordpress.3dn.nl/2011/06/24/scooba-doo-underwater-scooter/</link>
		<comments>http://wordpress.3dn.nl/2011/06/24/scooba-doo-underwater-scooter/#comments</comments>
		<pubDate>Fri, 24 Jun 2011 13:09:03 +0000</pubDate>
		<dc:creator>Tracy Leeflang</dc:creator>
				<category><![CDATA[gadgets]]></category>

		<guid isPermaLink="false">http://wordpress.3dn.nl/?p=2229</guid>
		<description><![CDATA[Scuba-Doo releases a new type of underwater scooter.]]></description>
			<content:encoded><![CDATA[<p><a href="http://wordpress.3dn.nl/files/2011/06/submersable2.jpg"><img class="alignnone size-medium wp-image-2232" src="http://wordpress.3dn.nl/files/2011/06/submersable2-248x300.jpg" alt="" width="248" height="300" /></a></p>
<p>Looking for something just a little different on your vacation this year? Check out Scooba-Doo&#8217;s new line of underwater scooters!</p>
<p>The obvious advantage of the ingenious design is the enclosed head area &#8211; the need for a mask or mouthpiece is eliminated by the constant replenishment of air into the clear dome that enables normal breathing. There&#8217;s also no need to wear weight-belts or an air tank.</p>
<p><a href="http://wordpress.3dn.nl/files/2011/06/scuba-doo-side-shot11.jpg"><img class="alignnone size-full wp-image-2234" src="http://wordpress.3dn.nl/files/2011/06/scuba-doo-side-shot11.jpg" alt="" width="258" height="195" /></a></p>
<p>Add the ability to cruise at a rate of 2.5 knots, or remain stationary while feeding the fish and you have a unique underwater experience open to a wider range of people than conventional diving &#8211; those who are not strong swimmers or who have minor disabilities can still ride the bike and you can wear your spectacles or contact lenses without difficulty. Even young children can easily use a Scuba-Doo.</p>
<p>It also comes in 5 cool colors:</p>
<p><a href="http://wordpress.3dn.nl/files/2011/06/scubacol.jpg"><img class="alignnone size-medium wp-image-2231" src="http://wordpress.3dn.nl/files/2011/06/scubacol-300x224.jpg" alt="" width="300" height="224" /></a></p>
<p>Check it out at <a href="http://www.scubadoo.com.au/">Scuba-Doo</a></p>
]]></content:encoded>
			<wfw:commentRss>http://wordpress.3dn.nl/2011/06/24/scooba-doo-underwater-scooter/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Circular Saw Pizza Cutter</title>
		<link>http://wordpress.3dn.nl/2011/06/24/circular-saw-pizza-cutter/</link>
		<comments>http://wordpress.3dn.nl/2011/06/24/circular-saw-pizza-cutter/#comments</comments>
		<pubDate>Fri, 24 Jun 2011 12:44:19 +0000</pubDate>
		<dc:creator>Tracy Leeflang</dc:creator>
				<category><![CDATA[gadgets]]></category>

		<guid isPermaLink="false">http://wordpress.3dn.nl/?p=2226</guid>
		<description><![CDATA[You hate those REALLY crusty bottom pizzas? Here's the fix!]]></description>
			<content:encoded><![CDATA[<p><a href="http://wordpress.3dn.nl/files/2011/06/saw-pizza-cutter.jpg"><img class="alignnone size-medium wp-image-2227" src="http://wordpress.3dn.nl/files/2011/06/saw-pizza-cutter-300x283.jpg" alt="" width="300" height="283" /></a></p>
<p>Now you, too, can look like a Manly man while serving up your favorite snack, with the Pizza Boss 3000 circular saw pizza cutter.</p>
<p>Here at <a href="http://www.perpetualkid.com/pizza-boss-3000-pizza-slicer.aspx">perpetualkid.com</a></p>
]]></content:encoded>
			<wfw:commentRss>http://wordpress.3dn.nl/2011/06/24/circular-saw-pizza-cutter/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>App Magnets</title>
		<link>http://wordpress.3dn.nl/2011/06/24/app-magnets/</link>
		<comments>http://wordpress.3dn.nl/2011/06/24/app-magnets/#comments</comments>
		<pubDate>Fri, 24 Jun 2011 12:32:19 +0000</pubDate>
		<dc:creator>Tracy Leeflang</dc:creator>
				<category><![CDATA[gadgets]]></category>

		<guid isPermaLink="false">http://wordpress.3dn.nl/?p=2223</guid>
		<description><![CDATA['Sticky' app.]]></description>
			<content:encoded><![CDATA[<p><a href="http://wordpress.3dn.nl/files/2011/06/appmag.jpg"><img class="alignnone size-medium wp-image-2224" src="http://wordpress.3dn.nl/files/2011/06/appmag-300x270.jpg" alt="" width="300" height="270" /></a></p>
<p>Whatever you want to do&#8230;  There&#8217;s an app for that.  That&#8217;s right.  Whether you want to buy movie tickets, read the news, write a review, pay a tip, catch a taxi, or even train your dog, there&#8217;s an app to help you out.</p>
<p>But what if you want to hang a photo on the fridge, leave a note on the microwave, or save that Chinese take-out menu in a place where you can&#8217;t lose it?  When it came to certain old-school tasks, the apps were coming up short.</p>
<p>A little reverse innovation was needed.  Well, good news, everyone&#8230; Now there&#8217;s an app for that, too.  All 18 individual epoxy coated App Magnets measure a substantial 7/8&#8243; x 7/8&#8243; and are waiting to hold that next photo or coupon!</p>
<p>Available at <a href="http://www.perpetualkid.com/app-magnets.aspx"> perpetualkid.com</a></p>
]]></content:encoded>
			<wfw:commentRss>http://wordpress.3dn.nl/2011/06/24/app-magnets/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Glitter Bubbles!</title>
		<link>http://wordpress.3dn.nl/2011/06/24/glitter-bubbles/</link>
		<comments>http://wordpress.3dn.nl/2011/06/24/glitter-bubbles/#comments</comments>
		<pubDate>Fri, 24 Jun 2011 09:14:42 +0000</pubDate>
		<dc:creator>Tracy Leeflang</dc:creator>
				<category><![CDATA[gadgets]]></category>
		<category><![CDATA[bubbles]]></category>
		<category><![CDATA[gadget]]></category>

		<guid isPermaLink="false">http://wordpress.3dn.nl/?p=2219</guid>
		<description><![CDATA[We&#8217;re all familiar with traditional bubbles. You know how it goes, you stick the wand in, blow, bubble forms, floats around for a bit and then falls to the floor...]]></description>
			<content:encoded><![CDATA[<p><a href="http://wordpress.3dn.nl/files/2011/06/bubbles.jpg"><img class="alignnone size-medium wp-image-2220" src="http://wordpress.3dn.nl/files/2011/06/bubbles-300x300.jpg" alt="" width="300" height="300" /></a></p>
<p>We&#8217;re all familiar with traditional bubbles. You know how it goes, you stick the wand in, blow, bubble forms, floats around for a bit and then falls to the floor and pops! How completely boring is that?</p>
<p>Introducing Touchabubbles! These bubbles really stick around! Blow them as you would ordinary bubble soap, but instead of popping, these harden up when exposed to air, making them strong enough to stack, roll, even throw.</p>
<p>Great idea for a party!</p>
<p>Get yours at <a href="http://www.perpetualkid.com/glitter-touchabubbles.aspx">perpetualkid.com</a></p>
]]></content:encoded>
			<wfw:commentRss>http://wordpress.3dn.nl/2011/06/24/glitter-bubbles/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Get your own domain&#8230; extension!</title>
		<link>http://wordpress.3dn.nl/2011/06/20/get-your-own-domain-extension/</link>
		<comments>http://wordpress.3dn.nl/2011/06/20/get-your-own-domain-extension/#comments</comments>
		<pubDate>Mon, 20 Jun 2011 12:07:51 +0000</pubDate>
		<dc:creator>Fred Leeflang</dc:creator>
				<category><![CDATA[internet]]></category>
		<category><![CDATA[domain]]></category>
		<category><![CDATA[icann]]></category>

		<guid isPermaLink="false">http://wordpress.3dn.nl/?p=2081</guid>
		<description><![CDATA[ICANN lifts domain name extension restrictions]]></description>
			<content:encoded><![CDATA[<p>Have you ever gotten your own .com or .nl domain? It may have seemed somewhat special at the time. These days it&#8217;s quite common and just about anybody can easily get their own domain for 10 bucks a year or so.</p>
<p>The Internet Corporation for Assigned Names and Numbers (ICANN) has just given us all an entirely new gadget however. Despite, and perhaps because, raging debates about whether online pornography should be put under a .xxx extension, ICANN has just announced that it will now be possible to get your own domain extension, so sometime soon it may be possible to go to, say, http://cocacola</p>
<p>Getting your own domain extension will not be cheap however. The ICANN application fee alone is $185.000 with an ongoing fee of $25.000 to maintain the domain extension.</p>
]]></content:encoded>
			<wfw:commentRss>http://wordpress.3dn.nl/2011/06/20/get-your-own-domain-extension/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>War!!</title>
		<link>http://wordpress.3dn.nl/2011/06/20/war/</link>
		<comments>http://wordpress.3dn.nl/2011/06/20/war/#comments</comments>
		<pubDate>Mon, 20 Jun 2011 11:41:58 +0000</pubDate>
		<dc:creator>Fred Leeflang</dc:creator>
				<category><![CDATA[security]]></category>
		<category><![CDATA[war]]></category>

		<guid isPermaLink="false">http://wordpress.3dn.nl/?p=2073</guid>
		<description><![CDATA[Operation Anti-Security]]></description>
			<content:encoded><![CDATA[<p>While a dramatic subject at first and possibly a bit of a &#8216;pffffft is that all?&#8217; after reading this first paragraph, hackergroup LulzSec <a title="War Declaration" href="http://ht.ly/5lxzI" target="_blank">has declared war</a> against white hat hackers and governments.</p>
<blockquote><p>﻿As we&#8217;re aware, the government and whitehat security terrorists across the world continue to dominate and control our Internet ocean. Sitting pretty on cargo bays full of corrupt booty, they think it&#8217;s acceptable to condition and enslave all vessels in sight. Our Lulz Lizard battle fleet is now declaring immediate and unremitting war on the freedom-snatching moderators of 2011.</p></blockquote>
<p>While arguably the whole statement sounds a bit immature, does LulzSec have a point? And are they as immature as the appear to be? Jack Koziol of the Infosec Institute says he&#8217;s quite sure they&#8217;re not and the members of the group are old school and masters in hiding their tracks. &#8220;I say these guys have been in the underground for years&#8221;, he claims.</p>
]]></content:encoded>
			<wfw:commentRss>http://wordpress.3dn.nl/2011/06/20/war/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Playing with Simple Facebook Connect</title>
		<link>http://wordpress.3dn.nl/2011/06/16/playing-with-simple-facebook-connect/</link>
		<comments>http://wordpress.3dn.nl/2011/06/16/playing-with-simple-facebook-connect/#comments</comments>
		<pubDate>Thu, 16 Jun 2011 22:23:19 +0000</pubDate>
		<dc:creator>Fred Leeflang</dc:creator>
				<category><![CDATA[coding]]></category>
		<category><![CDATA[internet]]></category>
		<category><![CDATA[wordpress]]></category>
		<category><![CDATA[api]]></category>
		<category><![CDATA[dutchie]]></category>
		<category><![CDATA[facebook]]></category>
		<category><![CDATA[fbml]]></category>

		<guid isPermaLink="false">http://wordpress.3dn.nl/?p=1996</guid>
		<description><![CDATA[Playing with Simple Facebook Connect]]></description>
			<content:encoded><![CDATA[<div>
<p>As a brief note to myself and other who may experience the same problem: I use the <a title="Simple Facebook Connect" href="http://ottopress.com/wordpress-plugins/simple-facebook-connect/" target="_blank">&#8220;Simple Facebook Connect (SFC)&#8221;</a> WordPress plugin on my site. It&#8217;s a very nice modularly written plugin but that&#8217;s all described very well on the site. My note is about using this plugin on a WordPress instance running multiple domains however.</p>
<p>While adding &#8216;like&#8217; buttons to my second domain on the server, I noticed I started getting error message from Facebook saying:</p>
<blockquote><p>﻿The app ID specified within the &#8220;fb:app_id&#8221; meta tag is not allowed on this domain. You must setup the Connect Base Domains for your app to be a prefix of http://dutchie.org/rowan-lore/.</p></blockquote>
<p>Further investigation showed that the &lt;fb:like tag generated by plugin adds an href parameter which contains the http://dutchie.org/rowan-lore/ URL.</p>
<p>As http://dutchie.org is really running on http://dutchie.3dn.nl but mapped on there with the domain mapping plugin, I figured I&#8217;d change the href parameter into http://dutchie.3dn.nl/rowan-lore. This can be done through a bit of an undocumented feature in SFC. I modify the theme files directly through putting an sfc_like() call in the theme file. When used without arguments, the sfc_like() function will generate the URL based on the get_permalink() WordPress API call. However, noticing the function also parses a $args parameter when provided, I decided to do:</p>
<pre class="brush: php; title: ; notranslate">
$url = urlencode(str_replace('dutchie.org ', 'dutchie.3dn.nl ', get_permalink($id))); sfc_like_button(array('url' =&amp;gt; $url));
</pre>
<p>While of course this little trick is handy to know and resulted in the desired effect, the error message remains.</p>
<p>I&#8217;ve also been looking into the FBML tags being put in the header:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;meta property='fb:app_id' content='100842573341270' /&gt;
&lt;meta property=&amp;quot;og:site_name&amp;quot; content=&amp;quot;Dutchie&amp;quot; /&gt;
&lt;meta property='og:url' content='http://dutchie.org/rowan-lore/' /&gt;
</pre>
<p>And manually tried to force sfc-base.php to generate dutchie.3dn.nl instead of dutchie.org in the og:url field. Alas, this also did not work. Otto, the developer of SFC assured me that there is no way of adding a second domain to a single FB application, but I&#8217;m still curious about this as http://dutchie.org is a second domain on the server indeed, but it&#8217;s *mapped* to http://dutchie.3dn.nl which is a subdomain of the facebook app.</p>
</div>
]]></content:encoded>
			<wfw:commentRss>http://wordpress.3dn.nl/2011/06/16/playing-with-simple-facebook-connect/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Stuxnet Anatomy</title>
		<link>http://wordpress.3dn.nl/2011/06/14/stuxnet-anatomy/</link>
		<comments>http://wordpress.3dn.nl/2011/06/14/stuxnet-anatomy/#comments</comments>
		<pubDate>Tue, 14 Jun 2011 22:32:44 +0000</pubDate>
		<dc:creator>Fred Leeflang</dc:creator>
				<category><![CDATA[security]]></category>
		<category><![CDATA[anatomy]]></category>
		<category><![CDATA[computer virus]]></category>
		<category><![CDATA[stuxnet]]></category>

		<guid isPermaLink="false">http://wordpress.3dn.nl/?p=1991</guid>
		<description><![CDATA[Stuxnet Animation]]></description>
			<content:encoded><![CDATA[<p>Stuxnet is considered the most advanced computer virus right now. Patrick Clair has created the video below for ABC Networks. Unfortunately that page does not work for me so somebody has put it on Youtube. Enjoy.</p>
<p>[There is a video that cannot be displayed in this feed. <a href="http://wordpress.3dn.nl/2011/06/14/stuxnet-anatomy/">Visit the blog entry to see the video.]</a></p>
]]></content:encoded>
			<wfw:commentRss>http://wordpress.3dn.nl/2011/06/14/stuxnet-anatomy/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Facebook Associates</title>
		<link>http://wordpress.3dn.nl/2011/06/01/facebook-associates/</link>
		<comments>http://wordpress.3dn.nl/2011/06/01/facebook-associates/#comments</comments>
		<pubDate>Wed, 01 Jun 2011 09:35:32 +0000</pubDate>
		<dc:creator>Fred Leeflang</dc:creator>
				<category><![CDATA[internet]]></category>
		<category><![CDATA[social]]></category>
		<category><![CDATA[appid]]></category>
		<category><![CDATA[developers]]></category>
		<category><![CDATA[email]]></category>
		<category><![CDATA[facebook]]></category>
		<category><![CDATA[friends]]></category>
		<category><![CDATA[functionality]]></category>
		<category><![CDATA[identity]]></category>
		<category><![CDATA[iframes]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[plugin]]></category>
		<category><![CDATA[sdk]]></category>
		<category><![CDATA[wordpress]]></category>
		<category><![CDATA[wordpress plugin]]></category>

		<guid isPermaLink="false">http://wordpress.3dn.nl/?p=1905</guid>
		<description><![CDATA[Facebook Application development combined with Wordpress]]></description>
			<content:encoded><![CDATA[<p>﻿I&#8217;m currently in the process of setting up the <a title="3DN Politics" href="http://politics.3dn.nl" target="_blank">3DN Politics</a> site, a site through which friends connected to each other through Facebook can automatically share articles they publish on their friends&#8217; walls. Essentially, this site lets associates sign up to the site by using their facebook identity to sign up to the site and allowing a Facebook application certain privileges.</p>
<p>I will use this article as a scratchpad for those developers willing to learn more about the Facebook <a title="Facebook Graph API" href="http://developers.facebook.com/docs/reference/api/" target="_blank">Graph API</a> and the <a title="Facebook PHP SDK" href="https://github.com/facebook/php-sdk/" target="_blank">Facebook PHP SDK</a> as I go, hoping that it will be useful.</p>
<h3>Facebook PHP SDK 3.0.1</h3>
<p>There&#8217;s a choice to be made here; We can develop a full WordPress plugin and embed the Facebook PHP API functionality in it or we can simply work on building a standalone Facebook IFRAMES app. I&#8217;ve chosen the latter for simplicity but I may later incorporate this into a WordPress plugin.</p>
<p>I&#8217;ll assume you have some basic understanding of how to create a facebook app and where to find the appid and the app secret.</p>
<p>So in the <em>index.php </em>of your app, start with setting up the following and add your own app ID, secret etc.:</p>
<pre class="brush: php; title: ; notranslate">
$appId  = &quot;[3DN APP ID]&quot;;
$secret = &quot;[3DN APP SECRET&quot;;
$canvasurl = &quot;http://3dn.nl/3dnfbcanvas/&quot;;
$canvas = &quot;http://apps.facebook.com/driedncanvas/&quot;;
$scope  = &quot;user_website,email,publish_stream,user_birthday,user_location,user_work_history,user_about_me,user_hometown,manage_pages,offline_access&quot;;
include_once &quot;facebook-php-sdk/src/facebook.php&quot;; # or wherever your source lives
$facebook = new Facebook (array(
  'appId' =&gt; $appid,
  'secret' =&gt; $secret,
  // 'cookie' =&gt; 'true',
));
$user = $facebook-&gt;getUser();
if ($user) {
    try {
        $user_profile = $facebook-&gt;api('/me');
    } catch (FacebookApiException $e) {
        $user = null;
}

if (!$user) {
    $loginUrl = $facebook-&gt;getLoginUrl(
        array(
            'scope' =&gt; $scope,
            'redirect_uri' =&gt; $canvas,
            // 'canvas' =&gt; .... ,
            // 'fbconnect' =&gt; .....,
            // 'next' =&gt; ....
        )
    );
print &lt;&lt;&lt;EOM
  &lt;script lang=&quot;javascript&quot;&gt;
    top.location.href = '$loginUrl';
  &lt;/script&gt;
EOM;
}
</pre>
<p>First we see some variables being set. The variables $appId and $secret get set to to the values Facebook assigned to your Facebook app.</p>
<p>Facebook literally defines a 'canvas' for which the URL sits on facebook.com. It's an empty canvas with the Facebook chrome around it. The canvas gets painted with the contents of the canvas URL, which in case of an iframe application typically sits on a non-Facebook site. Many people around the internet seem to label the canvas as the canvas URL; this is not the way Facebook's documentation speaks about it.</p>
<p>You may also have noticed that $cookie is commented out; Many facebook app writers used to include this in their code and filter some important settings out of the cookie value. This is no longer required per the 3.0.1 PHP SDK as everything is now part of the session layer.</p>
<p>Similarly but more importantly, the 'canvas', 'fbconnect' and 'next' options in the call to getLoginUrl() have been <strong>obsoleted</strong> as of version 3.0.1.</p>
<p>The $facebook-&gt;getUser() call checks if the user is logged in on our Facebook application, essentially through checking the session layer for the right variables to be in there and returning a non-null user-id if there are. Technically 'logged in on our Facebook application' is not the proper thing to say as there needs to be a combination of two things; <strong>Authentication</strong> (on facebook itself) and <strong>Authorization</strong> (of the app). getUser() may well have remembered something from an earlier access but in between anything may have happened. If getUser() returns a null user-id, we know for sure that the user has not authenticated yet. If it's non-null, we simply try to access the Facebook Graph API through calling a simple API function api('/me') to make sure the user is still authenticated in Facebook.</p>
<p>If the user is not authenticated in Facebook, we make sure that he does by calling $facebook-&gt;getLoginUrl() and redirecting the user to the URL returned by this function. Typically, getLoginUrl() will return the URL of our canvas along with some parameters to give our app the proper authentication AND authorization. This authentication is defined by the 'scope' parameter; furthermore we give getLoginUrl() the redirect_uri so it knows where to send the user to when authorization is done.</p>
<p>So after this code segment, we know authorization is done (otherwise Facebook won't send the user back to the redirect_uri, which contains our code; We could of course also send the user back to another page but we've made sure to return him to the canvas so we stay inside the Facebook chrome)</p>
<h3>WordPress</h3>
<p>Now, what I really want this app to do is to give people the opportunity to create their user account on the 3DN Politics site fully automated. This is also not a very difficult thing to do as there is a <a title="wp_create_user" href="http://codex.wordpress.org/Function_Reference/wp_create_user" target="_blank">WordPress API call</a> for it which requires only three arguments, username, password and email address.</p>
<p>As this Facebook app will not be a WordPress plugin as of yet, we need to apply a <a title="Using WordPress API from outside WordPress" href="http://www.webopius.com/content/139/using-the-wordpress-api-from-pages-outside-of-wordpress" target="_blank">little trick</a> and add the following to the source:</p>
<pre class="brush: php; title: ; notranslate">
define('WP_USE_THEMES', false);
require('../wp-blog-header.php');
status_header(200);
</pre>
<p>This will give us access to the entire WordPress API, including the <em>wp_create_user()</em> function. One thing of particular interest is the status_header() call. As I'm writing the code partially as a WordPress plugin I've decided to simply make a folder under the WordPress plugin directory and 'Alias' the Facebook application to '/3dnfbcanvas/'. The funny thing is that when we include the WordPress API, it will check if /3dnfbcanvas/ is a valid page <strong>in WordPress</strong>; as it is not, WordPress will log a 404 error and the outcome seems to be different per browser. Through forcing WordPress to log a 200 (everything's okay!), the outcome becomes predictable again.</p>
<p>Now it's time to see if we can actually first get the user information. We do this by using an <a title="FQL API" href="http://developers.facebook.com/docs/reference/fql/" target="_blank">FQL query</a>:</p>
<pre class="brush: php; title: ; notranslate">
try {
  $fql = &quot;select name, hometown_location, sex, pic_square, email from user where uid=&quot; . $user;
  $param = array (
    'method' =&gt; fql.query,
    'query' =&gt; $fql,
    callback =&gt; '');
  $fqlResult = $facebook-&gt;api($param);
} catch (Exception $o) {
  error($o)
}
</pre>
<p>Here, error() is just a function to display an error message, implement that any way you wish. After this snippet of code, <em>$fqlResult </em>now holds the values of the user's name, hometown location, sex, pic_square and email which we can all stuff into our WordPress database.</p>
<p>We do a little bit of filtering on the user's name:</p>
<pre class="brush: php; title: ; notranslate">
    $wpusername = strtolower($fqlResult[0]['name']);
    $wpusername = str_replace(' ', '', $wpusername);
    $wpusername = str_replace('.', '', $wpusername);
    $wpusername = str_replace('-', '', $wpusername);
    $email = $fqlResult[0]['email'];
</pre>
<h3>WordPress Plugin</h3>
<p>While working on this project I&#8217;ve gradually come to realize that it must have at least two parts to it:</p>
<ul>
<li>A wordpress plugin part</li>
<li>A facebook application part</li>
</ul>
<p>While it&#8217;s easy to use the above Facebook stuff to do some stuff like signing up in a highly automated manner, thus lowering the threshold, at some point it also becomes necessary to give associates a more fine grained control over their setup. So I have now created a Subversion repository which holds a few files for the WordPress plugin as well as the facebook application.</p>
<p>Using this we can now give the user a selection form for which <em>Associates</em> enabled site to sign up for:</p>
<pre class="brush: php; title: ; notranslate">
    $associates_sites = $wpdb-&gt;get_results(&quot;SELECT blog_id FROM wp_blogs&quot;);
    if (!$associates_sites) {
        echo 'there was an error querying the WordPress database';
        exit;
    } else {
        echo '&lt;select&gt;';
        foreach ($associates_sites as $associates_site) {
            $the_blog_id = (int)$associates_site-&gt;blog_id;
            $info = get_blog_details($the_blog_id);
            switch_to_blog($the_blog_id,true);
            $associates_settings = get_option('associates_settings');
            restore_current_blog();
            if ($associates_settings['associates_enabled']) {
                echo '&lt;option value=&quot;' . $info-&gt;blog_id . '&quot;&gt;' . $info-&gt;blogname . '&lt;/option&gt;';
            }
        }
        echo '&lt;/select&gt;';
</pre>
<h3>Simple Facebook Connect</h3>
<p>I&#8217;ve used the Simple Facebook Connect plugin for a little while already and am quite happy with it. It adds functionality for people so login using their facebook login. As I want people to use their Facebook login for Associates, I will use this plugin&#8217;s method of &#8216;connecting a facebook user to a wordpress user&#8217;. This way I won&#8217;t have to implement login buttons myself and the intended value of SFC stays unchanged.</p>
<p>Inspecting the code of SFC&#8217;s <em>sfc-login.php:</em></p>
<pre class="brush: php; title: ; notranslate">

&lt;td&gt;&lt;p&gt;&lt;fb:login-button perms=&quot;email&quot; v=&quot;2&quot; size=&quot;large&quot; onlogin=&quot;sfc_login_update_fbuid(0);&quot;&gt;&lt;fb:intl&gt;&lt;?php _e('Connect this WordPress account to Facebook', 'sfc'); ?&gt;&lt;/fb:intl&gt;&lt;/fb:login-button&gt;&lt;/p&gt;&lt;/td&gt;
</pre>
<p>This fragment adds a button to the user&#8217;s profile in WP allowing them to &#8216;connect their WP account to Facebook&#8217;. On clicking it a bit of JS <em>sfc_login_update_fbuid(0)</em> is executed. Inspecting the JS function <em>sfc_login_update_fbuid()</em> a little closer:</p>
<pre class="brush: jscript; title: ; notranslate">
        function sfc_login_update_fbuid(disconnect) {
            var ajax_url = '&lt;?php echo admin_url(&quot;admin-ajax.php&quot;); ?&gt;';
            if (disconnect == 1) {
                var fbuid = 0;
            } else {
                var fbuid = 1; // it gets it from the cookie
            }
            var data = {
                action: 'update_fbuid',
                fbuid: fbuid
            }
            jQuery.post(ajax_url, data, function(response) {
                if (response == '1') {
                    location.reload(true);
                }
            });
        }
</pre>
<p>So we see here that all that&#8217;s happening here is to POST some data, &#8216;action&#8217; and &#8216;fbuid&#8217; to admin-ajax.php, which should be easy to do from our Facebook application as well. It is in fact much easier even than having to use Ajax and such, as the Ajax code gets executed by SFC when the user clicks the connect link in SFC to connect their WordPress account with their Facebook account. All we need to do as we will make this choice for the WordPress user is:</p>
<pre class="brush: php; title: ; notranslate">
update_user_meta($wpuid, 'fbuid', $user);
</pre>
<p>I also decided that I don&#8217;t really want people who signed up as associates, and who will therefore automatically have had their Facebook uid connected to their WP user, to disconnect, so I made this impossible by setting:</p>
<pre class="brush: php; title: ; notranslate">
define('SFC_ALLOW_DISCONNECT', false);
</pre>
<p>in wp-config.php. (Note to self: quotes around true or false DO matter)</p>
<h3>Facebook token</h3>
<p>What happens under water with all the Facebook authentication and authorization going on is really that there is an Oauth authorization going on. The Facebook PHP API hides this, fortunately, from our view but what happens in the process is that an Oauth &#8216;token&#8217; gets created on the Facebook site. This token has an expiration time and typically gets used only during the user&#8217;s session.</p>
<p>What the Associates application wants to do however is to share articles by Associates to the walls of other Associates on clever timing! Clever timing is not always to share it &#8216;right away&#8217; when an Associate has finished their article. We have to keep in mind that a group of facebook friends is really a geographically very broad group and&#8230; timezones become important!</p>
<p>So what we&#8217;ve done in the getLoginUrl() is to add an &#8216;offline_access&#8217; authorization to the scope. This will cause Facebook to generate a token that does not expire. We need this token to post on the user&#8217;s behalf when the user is not physically logged in. So after the user has authenticated/authorized, we will store this permanent authorization token in a safe place:</p>
<pre class="brush: php; title: ; notranslate">
update_user_meta($wpuid, 'fb_token', $facebook-&gt;getAccessToken());
</pre>
<h3>Checking &#8216;Associates enabled&#8217; sites</h3>
<p>Now we get into the more WordPress specific stuff. After we have created the user&#8217;s login on the 3DN network, we need to determine which sites to list and make selectable for the user in the selection box.</p>
<p>First of all, for a user to sign up as associate on an associates enabled site is that we need to check IF a site is associate enabled. So we start with cycling through all our available 3DN network sites using:</p>
<pre class="brush: php; title: ; notranslate">
// Build list of current user's friend ID's
$friends = $facebook-&gt;api('/me/friends');
$friendsdata = $friends['data'];
$friendlist = array();
foreach ($friendsdata as $friend) {
    $friendlist[$friend['id']] = (int)$friend['id'];
}

$associates_sites = $wpdb-&gt;get_col(&quot;SELECT blog_id FROM wp_blogs&quot;);
foreach ($associates_sites as $associates_site) {
    $wpblogid = (int)$associates_site;
    $info = get_blog_details($wpblogid);
    switch_to_blog($wpblogid,true);
    $associates_settings = get_option('associates_settings');
    restore_current_blog();
    if ($associates_settings['associates_enabled']) {
        ....
    }
}
</pre>
<p>Here we do a query for blog_id of all the blogs running on the 3DN network, we switch the context to each blog with switch_to_blog() and we fetch $associates_settings through using get_option(&#8216;associates_settings&#8217;). The associates_settings option is created when the Associates plugin has been activated on the site. Through the administrative interface of the plugin we also have the opportunity to temporarily enable/disable Associates even when the plugin is activated, so we check the value of the &#8216;associates_enabled&#8217; field to see if the Associates plugin is both active and enabled on the site.</p>
<p>The plugin on activation has also created a table to keep track of the so called &#8216;prime users&#8217; for every site. Between the switch_to_blog() and restore_current_blog() calls we can find the name of this table with <em>$wpdb-&gt;prefix.&#8217;associates_prime_users&#8217;</em>. Querying this table and comparing the results with the earlier found $friendlist array, we can now check if the current user is a facebook friend of one of the prime users for this site:</p>
<pre class="brush: php; title: ; notranslate">
$prime_usertable = $wpdb-&gt;prefix.'associates_prime_users';
$prime_users = $wpdb-&gt;get_col(&quot;SELECT um.meta_value FROM $prime_usertable pu, wp_usermeta um WHERE pu.user_id=um.user_id AND um.meta_key='fbuid'&quot;);
$prime_userlist = array();
$prime_friend = false;
foreach ($prime_users as $prime_user) {
    if (in_array((int)$prime_user,$friendlist)) {
        $prime_friend = true;
    }
}
</pre>
<p>So we can now user the $prime_friend variable to determine if we should disable the Associates site in the selection box.</p>
<h3>Add Some Javascript and AJAX</h3>
<p>I want the selection box to have a little check mark next to every site where the user is already an associate and to have this checkmark be toggled when the user clicks on the site, so in order to create the selection box:</p>
<pre class="brush: php; title: ; notranslate">
echo '&lt;select name=&quot;associates_sites[]&quot; multiple size=5 cols=&quot;20&quot;&gt;';
...
if ($wpuid) {
    $onit = array_key_exists($wpblogid, get_blogs_of_user($wpuid));
} else {
    $onit = 0;
}
$onit = ($onit)?'&amp;#x2713':'';
...
if ($associates_settings['associates_enabled']) {
    print &quot;&lt;option value=\&quot;{$info-&gt;blog_id}\&quot; onclick=\&quot;toggleAssociate({$wpuid})\&quot;&quot;;
    if (!$prime_friend) {
        print &quot; disabled&quot;;
    }
    print &quot;&gt;{$info-&gt;blogname} {$onit}&lt;/option&gt;&quot;;
}
</pre>
<p>Here we introduce some javascript <em>toggleAssociate()</em> as well, which should:</p>
<ul>
<li> Retrieve the option value from the listing</li>
<li> Send the option value as well as the user ID to an AJAX handler</li>
<li> Process the value returned from the AJAX handler and update the listing</li>
</ul>
<p>The function toggleAssociate() can be implemented easily:</p>
<pre class="brush: jscript; title: ; notranslate">
    wp_print_scripts (array('sack'));
?&gt;
&lt;script lang=&quot;javascript&quot;&gt;
function toggleAssociate(wpuid) {
    var clicked;
    clicked = sites_form.associates_sites.options[sites_form.associates_sites.selectedIndex].value;
    var mysack = new sack ('/3dnfbcanvas/canvasajaxhandler.php');
    mysack.method = 'POST';
    mysack.setVar (&quot;action&quot;, &quot;toggleassociate&quot;);
    mysack.setVar (&quot;blog_id&quot;, clicked);
    mysack.setVar (&quot;wp_uid&quot;, wpuid);
    mysack.runAJAX();
}
&lt;/script&gt;
</pre>
<p>Here we use WordPress&#8217; included SACK library to create an AJAX object and POST both <em>blog_id</em> and <em>wp_uid</em> to it when the user clicks an option in the select box.</p>
<p>In the actual AJAX handler we need to verify that the <em>wp_uid</em> actually has the <em>fbuid</em> set so we know at least that this user is connected to a real facebook profile. Additionally, when a user toggles his/her associates status on a site we will try posting a short status update to his/her wall using the permanent token.</p>
<pre class="brush: php; title: ; notranslate">
// Check if user is on selected blog already
$his_blogs = get_blogs_of_user($wpuid);
$isonit = false;
foreach ($his_blogs as $his_blog) {
    if ($his_blog-&gt;userblog_id == $new_blog_id)
        $isonit = true;
}

// Set up some facebook stuff
appId  = &quot;....&quot;;
$secret = &quot;....&quot;;
$canvasurl = &quot;http://3dn.nl/3dnfbcanvas/&quot;;
$canvas = &quot;http://apps.facebook.com/driedncanvas/&quot;;
require &quot;facebook-php-sdk/src/facebook.php&quot;;
$facebook-&gt;setAccessToken($fb_token);
if ($isonit) {
    // Try posting a status update using the user's token. If this fails we know they weren't really an associate
    // to begin with and we can fail quietly.
    try {
        $facebook-&gt;api(&quot;/$fbuid/feed&quot;, &quot;post&quot;, array(
            &quot;message&quot; =&gt; &quot;Stopped being an associate&quot;));
         remove_user_from_blog($wpuid, $new_blog_id);
    } catch (Exception $e) {
        dbg(&quot;ERROR&quot;, $e);
    }
} else {
    // Try posting a status update using the user's token. If this fails we know they weren't really an associate
    // to begin with and we can fail quietly.
    try {
        $facebook-&gt;api(&quot;/$fbuid/feed&quot;, &quot;post&quot;, array(
            &quot;message&quot; =&gt; &quot;Became an associate&quot;));
        add_user_to_blog($new_blog_id, $wpuid, 'author');
    } catch (Exception $e) {
        dbg(&quot;ERROR&quot;, $e);
    }
}
</pre>
]]></content:encoded>
			<wfw:commentRss>http://wordpress.3dn.nl/2011/06/01/facebook-associates/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

