This page created July 9th 2009
Originally posted @ http://www.thegood.com/social/index.php/category/blog/industry/thegood-com/
We’re pretty proud of our new site, so we thought we’d give some technical deets for all of our geek readers. We do hope the rest of you can appreciate it on a more laymen level.
The site is an aggregation engine that acquires feeds from both Wordpress and Yahoo Pipes. Pipes delivers Twitter and other social media feeds in a nice JSON wrapper to Flash. Wordpress data is acquired via XMLRPC. We made some custom modifications to enable it to deliver some specificity, however most of it was right there for the taking. A tip of the cap to Wordpress and its open source goodness.
We’re able to administer the entire site content using Wordpress as the CMS talking nicely with its Flash front. Given this, the site can function as our portfolio, social aggregate, blog, and company profile all wrapped up in a of sweet.
If you find any bugs or ideas for improvement we’d love to hear from you. More selfishly, If you’re a potential client and dig what we’re layin’ down, don’t hesitate to drop us a line. We’d love to get in bed with you.
Thanks, and enjoy – theGOOD team.
This page created May 15th 2009
So, you’ve got a client that wants to manage their website content. They want to be able to update every field on their Flash website, er I mean “experience”. But there’s a catch… they FEAR technology and complex interfaces. They feel that Wordpress is too complex, never mind a proprietary custom build CMS.
What’s a developer to do?
THIS:
1. Create a Google Docs Spreadsheet – For this example I’m using row 1 to hold the field vars.

2. Set it to publish as a webpage – Be sure to select CSV format. Be sure to set it to re-publish when any editing occurs.

3. Set up Yahoo Pipes to fetch the CSV data – Set appropriate data mapping, renaming and use Regex to ensure data is proper.

4. Publish Pipe as your favorite data feed – For this example I chose JSON.

5. Load JSON into your Flash app. BOOM!
Bladow, your client can now go on doing what they’ve always done — edit an excel doc ( Google Doc ). They make changes there, it changes on their fancy experiential website.
Genius.
This page created March 19th 2009
Consider this beta, and not heavily tested. Just wanted to get it out there while I’m still working on it.
For a recent project a client wanted more control over the display of their Wordpress navigation. They wanted to be able to use any font. They also wanted a dynamic fold-able navigation that enables access to all categories without re-loading the page. This was a perfect project for me since I use Wordpress all over the mofo place. For example, you can see the navigation in use over there to the right. Also, as I’ve stated previously, I’m a big fan of leveraging the many publishing outlets I already use. So, I buit the nav in Flash connecting to Wordpress via XMLRPC using some classes from http://mattism.com/. This essentially allows me to use Wordpress as a content management system for Flash. You could obviously see how this could be applied to entire sites, like I have with my homepage. I’ve thought about building this a Wordpress plugin, and maybe down the road I will, but I doubt it as I’ll probably jump ship and start another project per usual. Besides, everyone knows you can’t make money writing Wordpress plugins.
How It Works:
Flash calls _rpc.call(”wp.getCategories”) to xmlrpc.php which returns an array of categories. I use this array to create a bunch of MovieClip()s. These clips add TextFields as children, are sorted and have events applied to them that enable the interactions. Two fonts reside in the library. One for the top node and one for the child and grandchildren nodes.
Features [the current goods]:
- Dynamic – Works dynamically with Wordpress categories. You update your categories in Wordpress, they show properly in Flash
- Sorting – Dynamically sorts top nodes. Controlled by Wordpress plugin my category order. For this to work I had to make a small addition to the Wordpress xmlrpc.php, located in your Wordpress root folder, to return the category term order. Added line 2776 – $struct['order'] = $cat->term_order;
- Page recognition – Recognizes the page you’re on and dynamically opens navigation to the parent node of said page onload. I could have used XMLRPC tomake this call, I’m sure. However, I opted to pass in the page url via Flashvars and run a check to find a match. When a match is found the nav opens to it’s parent node.
- Folding – Uses Grant Skinner GTween for interactions.
- Multiple – Allows posts to live under multiple category nodes.
Wish List [the future goods]:
- Multiline – Currently only supports single line category titles, so you’re limited in char length
- Scrolling – Currently the length of your category list is limited to the length of the swf. I plan to add functions to enable scrolling of the list based on mouseY. This will free up the nav to be as long as you desire.
- Post count – Do people really use this though? Probably not as its annoying.
- Levels – Currently the nav only supports 3 levels. It would be nice to be infinite.
- Build in the rest of Wordpress feature support for tag cloud, recent comments etc.
Total devel time: 2 days, or about 12 hours.
I’d love to see where other people take the code and what people build with it.
Source Code:
wpNavMain.as
/**
* wpNavMain by Chris Teso. Mar 19, 2009
* Visit www.christeso.com/blog for documentation, updates and more free code.
*
*
* Copyright (c) 2009 Chris Teso
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
**/
package
{
import flash.display.*;
public class wpNavMain extends Sprite
{
/*
========================================================
| Constructor
========================================================
*/
public function wpNavMain ()
{
stage.align = StageAlign.TOP_LEFT;
// add nav
var wp:Wp = new Wp()
addChild( wp )
}
}
}
Wp.as
/**
* Wp by Chris Teso. Mar 19, 2009
* Visit www.christeso.com/blog for documentation, updates and more free code.
*
*
* Copyright (c) 2009 Chris Teso
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
**/
package
{
import com.gskinner.motion.*
import com.mattism.http.xmlrpc.*;
import com.mattism.http.xmlrpc.util.*;
import flash.filters.*;
import flash.media.*;
import flash.ui.*;
import flash.display.*;
import flash.events.*;
import flash.net.*;
import flash.utils.*;
import flash.geom.*;
import flash.text.*;
import fl.transitions.*;
import fl.transitions.easing.*;
import flash.system.SecurityPanel;
import flash.system.Security;
public class Wp extends Sprite
{
/*
========================================================
| Private Variables | Data Type
========================================================
*/
private var _navArray:Array = new Array();
private var _rpc:Connection;
private var _topFont:Font = new topFont();
private var _roadSign:Font = new roadSign();
private var _topFmt:TextFormat = new TextFormat()
private var _currUrl:String = ""
private var _hideTimer:Timer
private var _textYPad:int = 16
private var _navSpeed:Number = .2
/*
========================================================
| Constructor
========================================================
*/
public function Wp ()
{
loadRpc()
}
private function checkPage()
{
// load in title
var paramList:Object = this.root.loaderInfo.parameters;
// set var to hold text
var _currUrl:String = paramList["url"]
//_currUrl = "http://www.christeso.com/index.php/category/portfolio/truth/truth-found/"
// scan through array and open nav to that one
for( var i=0;i<_navArray.length;i++ )
{
//trace( "_currUrl = "+_currUrl+" - "+_navArray[i].link )
if( _navArray[i].link == _currUrl )
{
//trace( "found match!" )
// open it
if( _navArray[i].childMc != null )
{
showGrandChildren( MovieClip( _navArray[i].childMc ) )
}
if( _navArray[i].grandChildMc != null )
{
showGrandChildren( MovieClip( _navArray[i].grandChildMc.parent ) )
}
}
}
}
private function showGrandChildren( mc:MovieClip )
{
//trace( "opening grandchildren" )
// amount to go down
var yLeap:Number;
// total that will be open as long as the node is not already open
var _totOpen:int = mc.numChildren-1
//trace( "_totOpen = "+_totOpen )
// ok we can prob do this in one big loop
for( var i=0;i<_navArray.length;i++ )
{
// push down top nodes as long as they are below the top node you're on
if( _navArray[i].mc != null )
{
if( _navArray[i].mc.origY > MovieClip(mc.parent).origY )
{
yLeap = _navArray[i].mc.origY + ( _totOpen*_textYPad )
new GTween( _navArray[i].mc, _navSpeed, {y:yLeap} )
}
else
{
yLeap = _navArray[i].mc.origY
new GTween( _navArray[i].mc, _navSpeed, {y:yLeap} )
}
}
// push down child nodes as long as they are below the node you're on and are a child of the node you're on
if( _navArray[i].childMc != null )
{
if( _navArray[i].childMc.parent == mc.parent )
{
if( _navArray[i].childMc.origY > mc.origY )
{
yLeap = _navArray[i].childMc.origY + ( _totOpen*_textYPad )
new GTween( _navArray[i].childMc, _navSpeed, {y:yLeap} )
}
else
{
yLeap = _navArray[i].childMc.origY
new GTween( _navArray[i].childMc, _navSpeed, {y:yLeap} )
}
}
}
// make grandchildren visible
if( _navArray[i].grandChildMc != null )
{
if( _navArray[i].grandChildMc.parent == mc )
{
_navArray[i].grandChildMc.visible = true
new GTween( _navArray[i].grandChildMc, _navSpeed, {alpha:1} )
}
else
{
new GTween( _navArray[i].grandChildMc, _navSpeed/2, {alpha:0, autoHide:true} )
}
}
}
}
private function hideGrandChildren( e:Event )
{
// amount to go down
var yLeap:Number;
// ok we can prob do this in one big loop
for( var i=0;i<_navArray.length;i++ )
{
// push up top nodes
if( _navArray[i].mc != null )
{
yLeap = _navArray[i].mc.origY
new GTween( _navArray[i].mc, _navSpeed, {y:yLeap} )
}
// push up child nodes
if( _navArray[i].childMc != null )
{
yLeap = _navArray[i].childMc.origY
new GTween( _navArray[i].childMc, _navSpeed, {y:yLeap} )
}
// make grandchildren visible
if( _navArray[i].grandChildMc != null )
{
new GTween( _navArray[i].grandChildMc, _navSpeed/2, {alpha:0, autoHide:true} )
}
}
}
private function childClick( e:Event )
{
var i:int = e.currentTarget.indexNum
var srcRequest:URLRequest = new URLRequest( _navArray[i].link );
navigateToURL( srcRequest, "" );
}
private function childOver( e:Event )
{
trace( "mouse over "+MovieClip( e.currentTarget ).name )
// remove parent events
MovieClip( e.currentTarget ).parent.removeEventListener( MouseEvent.CLICK, childClick )
// stop close timer
_hideTimer.stop()
// show grandkids
showGrandChildren( MovieClip( e.currentTarget ) )
}
private function childOut( e:Event )
{
trace( "mouse out "+MovieClip( e.currentTarget ).name )
// add parent events
MovieClip( e.currentTarget ).parent.addEventListener( MouseEvent.CLICK, childClick )
// start timer for close
_hideTimer.start();
}
private function grandChildOver( e:Event )
{
trace( "mouse over "+MovieClip( e.currentTarget ).name )
// remove parent events
MovieClip( e.currentTarget ).parent.removeEventListener( MouseEvent.CLICK, childClick )
// handle its events
MovieClip( e.currentTarget ).addEventListener( MouseEvent.CLICK, childClick )
}
private function grandChildOut( e:Event )
{
trace( "mouse out "+MovieClip( e.currentTarget ).name )
// remove parent events
MovieClip( e.currentTarget ).parent.addEventListener( MouseEvent.CLICK, childClick )
// handle its events
MovieClip( e.currentTarget ).removeEventListener( MouseEvent.CLICK, childClick )
}
private function orderTop()
{
var yPos:int = 0;
var topCount:int = 0
// first we need to find all unique parents
for( var i=0;i<_navArray.length;i++ )
{
//
if( _navArray[i].mc != null )
{
// place the top
_navArray[i].mc.y = yPos
_navArray[i].mc.origY = yPos
// calculate the next Y pos
yPos = _navArray[i].mc.y + ( _navArray[i].mc.numChildren*_textYPad )+_textYPad
}
}
}
private function makeTopNode( i:int )
{
trace( "making top node "+_navArray[i].title+" id = "+_navArray[i].id+" parent id = "+_navArray[i].parentId )
// create an mc holder
var mc:MovieClip = new MovieClip()
mc.name = _navArray[i].title
// create a text field
var t:TextField = new TextField()
t.mouseEnabled = false
t.name = "text"
t.autoSize = TextFieldAutoSize.LEFT
t.selectable = false
t.embedFonts = true
t.antiAliasType = flash.text.AntiAliasType.ADVANCED
t.htmlText = _navArray[i].title.toUpperCase()
_topFmt.size = 13;
_topFmt.font = _roadSign.fontName;
_topFmt.color = 0x666666
//_topFmt.letterSpacing = .5
_topFmt.kerning = true;
t.setTextFormat( _topFmt )
mc.addChild( t )
addChild( mc )
_navArray[i].mc = mc
// events
mc.indexNum = i
mc.origY = mc.y
mc.buttonMode = true
mc.addEventListener( MouseEvent.CLICK, childClick )
// now loop through this level and populate kids
findChildren( _navArray[i].id, mc )
}
private function makeChild( i:int, par:MovieClip )
{
trace( " making child "+_navArray[i].title+" id = "+_navArray[i].id+" parent id = "+_navArray[i].parentId )
// create an mc holder
var mc:MovieClip = new MovieClip()
mc.name = _navArray[i].title
// create a text field
var t:TextField = new TextField()
t.mouseEnabled = false
t.name = "text"
t.autoSize = TextFieldAutoSize.LEFT
t.selectable = false
t.embedFonts = true
t.antiAliasType = flash.text.AntiAliasType.ADVANCED
t.htmlText = _navArray[i].title.toUpperCase()
_topFmt.size = 10;
_topFmt.font = _topFont.fontName;
_topFmt.color = 0xffffff
_topFmt.letterSpacing = .5
_topFmt.kerning = true;
t.setTextFormat( _topFmt )
_navArray[i].childMc = mc
mc.y = par.numChildren*_textYPad
mc.addChild( t )
par.addChild( mc )
// now loop through this level and populate kids
findGrandChildren( _navArray[i].id, mc )
// events
mc.indexNum = i
mc.origY = mc.y
mc.buttonMode = true
mc.addEventListener( MouseEvent.CLICK, childClick )
mc.addEventListener( MouseEvent.MOUSE_OVER, childOver )
mc.addEventListener( MouseEvent.MOUSE_OUT, childOut )
}
private function makeGrandChild( i:int, par:MovieClip )
{
trace( " making grandchild "+_navArray[i].title+" id = "+_navArray[i].id+" parent id = "+_navArray[i].parentId )
// create an mc holder
var mc:MovieClip = new MovieClip()
mc.name = _navArray[i].title
// create a text field
var t:TextField = new TextField()
t.mouseEnabled = false
t.name = "text"
t.autoSize = TextFieldAutoSize.LEFT
t.selectable = false
t.embedFonts = true
t.antiAliasType = flash.text.AntiAliasType.ADVANCED
t.htmlText = _navArray[i].title.toUpperCase()
_topFmt.size = 10;
_topFmt.font = _topFont.fontName;
_topFmt.color = 0x999999
_topFmt.letterSpacing = 0
_topFmt.kerning = true;
t.setTextFormat( _topFmt )
_navArray[i].grandChildMc = mc
mc.alpha = 0
mc.visible = false;
mc.x = 5
mc.y = par.numChildren*_textYPad
mc.addChild( t )
par.addChild( mc )
// events
mc.indexNum = i
mc.buttonMode = true
//mc.mouseChildren = false
//mc.addEventListener( MouseEvent.CLICK, childClick )
mc.addEventListener( MouseEvent.MOUSE_OVER, grandChildOver )
mc.addEventListener( MouseEvent.MOUSE_OUT, grandChildOut )
}
private function findGrandChildren( id:int, par:MovieClip )
{
for( var i=0;i<_navArray.length;i++ )
{
// hunt for children of the parent
if( _navArray[i].parentId == id )
{
//trace( "-- found child "+_navArray[i].title+" | id = "+_navArray[i].id+" | parent = "+_navArray[i].parentId )
// found one, now make a grandchild
makeGrandChild( i, par )
}
}
}
private function findChildren( id:int, par:MovieClip )
{
for( var i=0;i<_navArray.length;i++ )
{
// hunt for children of the parent
if( _navArray[i].parentId == id )
{
//trace( "-- found child "+_navArray[i].title+" | id = "+_navArray[i].id+" | parent = "+_navArray[i].parentId )
// found one, now make a child
makeChild( i, par )
}
}
}
private function findParents()
{
// first we need to find all unique parents
for( var i=0;i<_navArray.length;i++ )
{
// analyze the node... is it a top node?
if( _navArray[i].parentId == 0 && _navArray[i].title != "Uncategorized" )
{
//trace( "-- found parent "+_navArray[i].title )
// create a top node container
makeTopNode( i )
}
}
}
private function loadRpc()
{
_rpc = new ConnectionImpl('blogaddress/xmlrpc.php');
_rpc.addEventListener(Event.COMPLETE, rpcCompleteHandler);
_rpc.addEventListener(ErrorEvent.ERROR, rpcErrorHandler);
_rpc.addParam(0, XMLRPCDataTypes.INT); // Blog Id
_rpc.addParam("blogusername", XMLRPCDataTypes.STRING); // Username
_rpc.addParam("blogpassword", XMLRPCDataTypes.STRING); // Password
getCategories()
}
private function getCategories():void
{
_rpc.call("wp.getCategories")
}
private function rpcCompleteHandler(evt:Event):void
{
var response:Object = _rpc.getResponse();
for(var i:String in response)
{
// need to first grab all the top nav categories
trace( response[i].categoryName )
trace( response[i].htmlUrl )
trace( response[i].parentId )
trace( response[i].categoryId )
trace( response[i].order )
trace( "------------------" )
_navArray.push( { title:response[i].categoryName, link:response[i].htmlUrl, id:response[i].categoryId, parentId:response[i].parentId, order:response[i].order } )
}
// Sort the array according to your category order setting in WP
_navArray.sortOn( "order", Array.NUMERIC )
// setup close timer
_hideTimer = new Timer( 500, 1 );
_hideTimer.addEventListener( TimerEvent.TIMER, hideGrandChildren );
findParents()
orderTop()
checkPage()
}
private function rpcErrorHandler(evt:ErrorEvent):void
{
var fault:MethodFault = _rpc.getFault();
}
}
}
Download CS4 AS3 FLA and Classes
Enjoy.
This page created March 6th 2009

Over the last couple of years my addiction to taking pictures every day has grown in intensity. More recently this addiction has heightened my curiosity to a point of seriousity. You do realize that seriousity should have inclusion confirmation from Merriam-Webster. If truthiness can make in, seriousity should. Seriously. Ok, back to the point. I’m getting more serious about photography. I even purchased a serious camera. Along with this serious camera, and an overabundance of seriousity about it’s use, I’ve gone and constructed myself a website dedicated to my photography. It is my hope that this will inspire and urge potential clients to contact me about my services.
The concept of the site is to take as much distraction out of the interface as possible to allow all focus on the content, the photography. I decided the entire site could be controlled by a small non intrusive control. I also wanted users to be able to interact with the photography by zoom and panning. Users also have the option to zoom out to see the full photo. Finally, I wanted a super easy way to content manage the site. Since I’m an avid Flickr user, its API was a natural CMS choice. I merely have to tag my photos in Flickr and they show up categorized on my site. I’m a big fan of leveraging the many publishing outlets I already use.
If you are a photographer, and are interested in owning a site similar to this one, chirp me.
Chris Teso – Portland Photographer
This page created November 24th 2008

After much back and forth to the drawing board I’ve come up with an idea for my new site that satisfies several needs. Furthermore, I’ve gone ahead and built it. The two main needs for the site are as follows:
1. Maintain the ability to update the site easily by leveraging the many publishing outlets I already use.
2. Aggregate all of the content I create across multiple platforms into one simple easy to use interface.
The solution for number one was to use Yahoo Pipes to aggregate RSS feeds from the various publishing platforms I already use. These platforms are as follows:
1. Wordpress – runs my blog, my portfolio and my flash lab experiments
2. Flickr – my photography
3. Vimeo – my movies
4. Google Reader – what I read
Solution #1
Pipes allowed me to marry each disparate RSS feed into one long feed. This essentially allows me to use each platform as a content management system. I had to write a few ASP scripts to enable wordpress specific posts to be translated into an RSS feed before sending it off to Pipes to be assimilated.
Solution #2
I created a simple flash application that hit the Pipes feed and displayed content in an animation that resembled a stream of data or consciousness. Within flash I had to do some custom String manipulation to identify where the various pieces of content may be originating. Once these were classified it was as simple as building out the classes that would run the content manipulation. It was also nice to get to play around with the new 3D api built into Flash 10.
All and all I’m very pleased with the site. It will allow me to continue to use the publishing platforms I like, the platforms that are best at housing content, and still display the content in one central stream.
Check out the new site here : http://www.christeso.com.
Permalink: http://www.christeso.com/index.php/portfolio/istream-a-flickr-vimeo-wordpress-google-controlled-website/