Postcards From My Life

Lint I find in my mind's belly-button.
  • EPK
  • Consulting
  • Resume
  • Nerd Herding
  • Talks
  • Flex
  • Zend Framework

Archive for the ‘Programming’ Category

« Older Entries

Tips on how to get accepted as a speaker at a PHP conference

Friday, August 27th, 2010

Dear Reader,

I sit here this morning working on my sixth PHP conference (ZendCon 06, 07, 08, DPC 09, tekx and now ZendCon 10) I have to sit back and reflect on how lucky I am. I get paid to help select the sessions that developers from around the world will sit in and learn in. It is truly humbling when I think about it.

One of the few downsides to planning a conference though is that any given Call for Papers usually generates a nine to one ratio of proposals to speaking slots. Do the math and you will see that for every one person I get to make happy, eight more think I am a total douche, or worse. I call the email’s “Dear John’s” because “rejection letters” seems so ugly. No matter what you call them though, it is never a happy time.

I’ve sent out thousands of Dear John’s in the past 5 years. Most people simply sigh and move on. Occasionally David Coallier will rip on my for habitually rejecting him at every conference. (It’s not on purpose dude, I swear!) However, my favorite response is one I usually only get once per conference. Every now and then, someone will write me and ask “Ok, so how do I better my chances of being selected?” They really want to speak and want to know what it takes.

The selection committee has a responsibility to pick the topics and speakers that they know will give the attendees the highest return on their training dollar. That is why you see a lot of the same speakers at each big conference. Most of us know Derick Rethans, Sebastian Bergmann, Lorna Jane Mitchell, Nate Abele and Keith Casey. We know the types of talk they deliver but most importantly, we know that if they pitch a session they know their material.

So how does a new speaker break into the national level? Here is the advice I give each speaker that asks me after getting a Dear John, usually accompanied by specific advice on why their specific session was rejected if possible.

  1. Blog on your topic. Blog it a lot. Blog it until people think of you when they think of your topic.
  2. Start speaking at your local user group. Be willing to drive to the next city and speak at their’s also. User Groups are a great way to practice new material and user group leaders talk to each other. it won’t take long for you to gain a reputation.
  3. Get accepted at a regional conference. As insinuated above, selection committees don’t like to take a chance on an unknown unless the content is very compelling. The way to overcome that is not to be an unknown. Get accepted at a few regional conferences and make sure they are using joind.in for feedback. I can’t count the number of times I’ve referenced a speaker’s joind.in profile in the last two years to read the comments. (Consider it your speaker’s resume)
  4. Record your talks and put them on your web site. If nobody else will record them for you, do it yourself. Most cameras and even phones have 30-45 minutes of record time. Set one up in the corner and record yourself. Jeremy Brown has an excellent example of this on his presentations page. (And if you are into Zend Framework watch his “Zend Framework…without Inhaling” video. it’s worth the 45 minute investment.)
  5. When you submit to a national conference, in the “Additional notes” section, give me links to all of this. Yes it is true, most proposals get between 10 and 30 seconds. However, if yours catches the eye of the committee, don’t make them dig to find out who you are or if you are any good. Give them this info up front.

There, that is my advice to speakers who receive Dear John letters. If you want it bad enough, go get it, don’t just shrug and give up. Trust me, we want you to be a part of conferences, big and small.

Until next time,
I <3 |<

=C=

Tags: conference, Derick Rethans, joind.in, Keith Casey, lorna jane mitchell, Nate Abele, opinion, Sebastian Bergmann, speaking
Posted in PHP | 2 Comments »

 

Software development DSL

Friday, August 20th, 2010

Dear Reader,

[Author's Note: Fridays should be fun. If you are a manager, try taking one Friday a month and let your developers do whatever they want. Trust them to do something but don't give them any parameters at all. See what they dream up.]

When I venture out into the real world, the one not populated with developers, I get a lot of strange looks and odd stares when I talk. Apparently there are a lot of people out there who don’t grok grep, have never been slashdotted, and can’t tell a brown number from a WAG. I saw r0ml give a talk one year about domain specific languages and how they are important. I apologize to r0ml for bastardizing his idea but today I am going to share five of our more colorful terms from the software development DSL.
(more…)

Tags: funny, lexicon, Programming
Posted in Programming | 4 Comments »

 

5 PHP twitter accounts that have replaced my feed reader

Wednesday, August 11th, 2010

Dear Reader,

I have given up on feed readers. I never did like them much to begin with and have found over the past couple of years that twitter does a better job of showing me interesting blog posts than Google Reader ever did.

In addition to recommendations from friends however, there are a hand full of accounts that I follow to keep up with what is going on. So here is my #followFriday for the next few weeks all wrapped into a single blog post.
(more…)

Tags: ben ramsey, chris cornutt, matthew weier o'phinney, mwop, phpc, phpcamp.com, phpdeveloper.org, planet-php.org
Posted in PHP | 7 Comments »

 

Putting all the pieces together

Monday, August 9th, 2010

Dear Reader,

I have posted several posts on this blog that all derived from a sample app that I wrote to generate QR Codes using the Google API. So far I’ve only give you the individual pieces, this post is about pulling them all together and creating the application.

I have been working on this particular application since February. (I used it to generate the QR Code I sent to the lovely and talented Kathy for Valentine’s Day. (Yeah, I’m that much of a geek) If you are a regular reader then you will recognize some of the code and concepts that we have discussed previously.

The MXML can be found below this post, and a link to the FXP can be found just above it. If you have Flash Builder 4, grab the FXP and you can import it and see all the code.

Converting Flex Builder 3 to Flash Builder 4

This project actually started life as a Flex Builder 3 project. I did the normal Export/Import to get the project into Flash Builder 4 and this worked just fine. The code compiled and ran immediately. (Kudos to the Flash Builder 4 team for their work in backwards compatibility)

All was not perfect however. One of the cool new things about Flash Builder 4 is the Themes option. If you use the Spark components then themes will allow you to quickly change the look and feel of your application. If you right click on the project itself and then select Properties, you will notice a “Flex Theme” option.

From this dialog box you can choose from 13 different pre-defined themes and you can design your own.

Unfortunately, since I imported this app from Flex Builder 3, none of my visual controls were spark controls. The results of applying a theme was comical. Thanks to the code completion and some trial and error though, I was able to convert most of the controls over to spark. In most cases, the code completion actually told me “Don’t use X, use Y” which is a VERY NICE feature. The remaining MX controls did not have an obvious spark counterpart or the suggested counterparts didn’t work the way I wanted them to.

Setup and cleanup

A good portion of the Actionscript code in the MXML falls into 2 categories, either setup code or cleanup code.

Setup

[Bindable] protected var qrCode:QRCode = new QRCode();
 
var imageWindow:ImageWindow;
 
 protected function init():void
 {
         /*
         * add our event listeners
         */
         qrCode.addEventListener('imageCreated',this.displayImage);
         qrCode.addEventListener('imageFailed',this.cannotDisplayImage);
 } // protected function init():void

Here we create the QRCode class described in “Actionscript class to create QR Codes“. In the init() we add our event listeners for a successful image fetch and a fail.

Cleanup

		protected function destroy():void
		{
			try {
				imageWindow.close();
				imageWindow=null;
			} catch (e:Error) {
 
			}
		}

Here in destroy() we only do one thing, we close any open windows. Since this app only has one window, we only have to hard-code a close here. However, since the window may not be open, I’ve wrapped it in a try/catch so that if the window is not yet open, we handle the error. (Actually, we ignore the error)

The heart of the app

createImage() is the heart of the app. The MXML below it is just display code setting things up so that users can turn all the knobs and push all the buttons. Once they have everything the way they want, we call createImage().

the majority of the code is a big switch statement that puts together the right string to encode. The last one is the most difficult, the VCARD. [Side Note: Since I couldn't find an Actionscript VCARD implementation, I may write one in a future post.]

       /*
        * Setup for the call
        */
       qrCode.width                = parseInt(imageWidth.text);
       qrCode.height               = parseInt(imageHeight.text);
       qrCode.margin               = margin.selectedIndex;
       qrCode.errorCorrectionLevel = errorCorrectionLevel.selectedIndex;
       qrCode.content              = contentToEncode; 			
       qrCode.imageType            = grpImageType.selectedValue.toString();
 
 
       /*
        * make the call
        */
       qrCode.createImage();

Once we know what we are encoding, the code above actually makes the call. We set the properties for the qrCode and the call qrCode.createImage().

In the init() we set event listeners for both a successful image retrieval and a failure. Since every developer knows their code doesn’t fail, let’s talk about the success.

protected function displayImage(event:Event):void
{
       var thisImage:Image = event.target.getImage();
       var winX:int = -1; 
       var winY:int = -1;
 
       try {
               winX=imageWindow.nativeWindow.x;
               winY=imageWindow.nativeWindow.y;
               imageWindow.close();
               imageWindow = null;
       } catch (e:Error) {
 
       }
       imageWindow = new ImageWindow(thisImage,grpImageType.selectedValue.toString());
       if (winX>-1) {
               imageWindow.open();
               imageWindow.nativeWindow.x=winX;
               imageWindow.nativeWindow.y=winY;
       } else {
               imageWindow.open();
       }
       return;
} // protected function displayImage(event:Event):void

Again, as with in destroy() we try to close the window. However, before we do so, we save off the X and Y of the window in case the user has moved it.

Then we create a new ImageWindow, passing in the image and the type of image to save. Finally, we position it. We have to position it after the call to open because before open is called, the nativeWindow property doesn’t exist.

The package store

Now that everything is working, we need to build it, package it and get it ready to ship. Up till now, when we have been compiling the application, we have been using the F11 key, which fired the debug version. That is why we can only have one instance running at a time. If you use CTRL-F11 to run your app, it won’t include the debug code and won’t try to attach itself to the debugger. However, to package it and ship it you have a few more steps.

Code signing

The last thing most AIR developers seem to think about is that they need a certificate to sign their code. It is a topic that serious application developers should tackle. I looked around at the web and found this page as the best reference for purchasing code signing certificates. The only one I could find that is not listed on that chart is GoDaddy. Unlike their SSL certificates, GoDaddy’s code signing certificates aren’t cheap. They start at $199/year.

No matter which service you go with, if you plan on distributing your application, you will need a certificate. For the purposes of this demonstration I will use a self signed certificate to package my application. This is not a recommended practice. (I’m just too cheap to pop for a cert for this tutorial)

To begin the export journey, click on the Project menu and select “Export Release Build”. This will bring up the first screen in the export wizard.

In this first screen we usually accept the defaults for everything. The one option of interest is the “Enable View Source” check box. Even though we are building an AIR application, we can still make our source code available for inspection. Checking the box enables the “Choose Source Files” button and you can decide to allow the user to browse the entire application or just certain files. When the user runs your application, a “View Source” option is added to the right click menu and if they choose it, they will see a code browser that looks like this:

As we discussed in the Code Signing section above, all AIR apps must be signed with a code signing certificate. Clicking “Next” on the wizard will bring up the code signing screen of the Wizard.

This is the screen where you select your certificate. If you are just testing, or distributing your application internally, you can choose to Create a certificate. While this will allow you to self-sign your application, when you install your users will see an ugly, scarry screen telling them that AIR via your operating system does not recognize the certificate of the publisher and thus cannot vouch for them.

This is generally not what you want when it comes to your customers.

You do have the option of creating an AIRI file. This is an intermediate file that you can hand off for signing. This is the option you would want if your company has a central authority that controls the signing certificates. They will sign the AIRI file using the command line utility adt and hand you back an AIR file ready to distribute.

Clicking “Next” will actually begin the export process. If you have warnings in your application (as the QRCodeMaker does) it will bring up a dialog box alerting you to those warnings and allowing you to cancel the process at that point and deal with them or continue on. Since ours is benign, I have chosen to ignore it.

The final step in the process is to choose which files get included the AIR or AIRI file. I assume this is to allow you to exclude common libraries and simply link to them. Our application is simple enough so that we just select the default which is the XML meta file and the generated SWF.

Clicking “Finish” will complete the export of your AIR or AIRI and you are ready to distribute or sign accordingly.

Conclusion

Developing desktop applications (and I assume mobile apps as wel) is never easy. Even trivial apps like QRCodeMaker require pre-planning to complete without several refacortings. The Flex framework, while verbose (as pointed out by my friends Nate Abele and Ed Finkler in the comments of the post “Actionscript class to create QR Codes“) is no more verbose than most modern frameworks. This is a problem I have with most modern frameworks for the languages with which I have worked. (Java, PHP and ActionScript) It is not necessarily a bad thing to be overly verbose but it does increase the learning curve on these frameworks.

The point of these posts has not been to show you best practices or even recommended practices. I myself am still discovering those. My point was to show you the process I went through to get this application running, my thought process behind some of the decisions I made, and to start a discussion.

I hope you will take the code I’ve shared with you, tinker with it, make it better and then share it with others.

Until next time,
I <3 |<
=C=

Disclosure: I work with Blue Parabola, Adobe is a customer of Blue Parabola.

Right Click to download the entire project
Completed (but self-signed) application.

QRCodeMaker.mxml

<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009" 
						xmlns:s="library://ns.adobe.com/flex/spark" 
						xmlns:mx="library://ns.adobe.com/flex/mx" 
						xmlns:net="flash.net.*" 
	                    creationComplete="init();" 
	                    width="250" 
	                    height="400"  
						showStatusBar="false"
						backgroundColor="#F0F0F0"
						closing="destroy()">
	<s:layout>
		<s:BasicLayout/>
	</s:layout>
 
	<fx:Declarations>
		<s:RadioButtonGroup id="grpImageType" />
	</fx:Declarations>
 
	<fx:Script>
	<![CDATA[
 
		import mx.controls.Alert;
		import mx.controls.Image;
		import mx.rpc.events.FaultEvent;
		import mx.rpc.events.ResultEvent;
		[Bindable] protected var qrCode:QRCode = new QRCode();
 
		var imageWindow:ImageWindow;
 
		 protected function init():void
		 {
			 /*
			 * add our event listeners
			 */
			 qrCode.addEventListener('imageCreated',this.displayImage);
			 qrCode.addEventListener('imageFailed',this.cannotDisplayImage);
		 } // protected function init():void
 
		protected function destroy():void
		{
			try {
				imageWindow.close();
				imageWindow=null;
			} catch (e:Error) {
 
			}
		}
		 protected function createImage():void
		 {
 
			var contentToEncode:String = ""; 
 
			/*
			 * Figure out which type of content we are encoding
			 */
			switch(tabBar.selectedIndex) {
		 		case 0:
		 			// TEXT
					contentToEncode  = this.textToEncode.text;
					break;
 
		 		case 1:
		 			// URL
					contentToEncode  = this.emailToEncode.text;
					break;
 
		 		case 2:
		 			// Email address
					contentToEncode  = 'MAILTO:'+this.urlToEncode.text;
					break;
 
		 		case 3:
		 			// vCard
		 		/*
				 * http://en.wikipedia.org/wiki/VCard
				 */
					contentToEncode = "BEGIN:VCARD\n" +
					                  "VERSION:3.0\n" +
					                  "N:"+txtLName.text+';'+txtFName.text+"\n" +
					                  "FN:"+txtFName.text+' '+txtLName.text+"\n" +
					                  "ADR:"+txtAddress.text+';'+txtCity.text+';'+txtState.text+';'+txtPostalCode.text+"\n" +
					                  "TEL:"+txtPhone.text+"\n" +
					                  "EMAIL:"+txtEmail.text+"\n" +
					                  "URL:"+txtUrl.text+"\n" +
					                  "END:VCARD\n";
					break;		 		
		 	}
 
			/*
			 * Setup for the call
			 */
			qrCode.width                = parseInt(imageWidth.text);
			qrCode.height               = parseInt(imageHeight.text);
			qrCode.margin               = margin.selectedIndex;
			qrCode.errorCorrectionLevel = errorCorrectionLevel.selectedIndex;
			qrCode.content              = contentToEncode; 			
			qrCode.imageType            = grpImageType.selectedValue.toString();
 
 
			/*
			 * make the call
			 */
			qrCode.createImage();
			return;			
		 } // protected function createImage():void
 
 
		 protected function displayImage(event:Event):void
		 {
			var thisImage:Image = event.target.getImage();
			var winX:int = -1; 
			var winY:int = -1;
 
			try {
				winX=imageWindow.nativeWindow.x;
				winY=imageWindow.nativeWindow.y;
				imageWindow.close();
				imageWindow = null;
			} catch (e:Error) {
 
			}
			imageWindow = new ImageWindow(thisImage,grpImageType.selectedValue.toString());
			if (winX>-1) {
				imageWindow.open();
				imageWindow.nativeWindow.x=winX;
				imageWindow.nativeWindow.y=winY;
			} else {
				imageWindow.open();
			}
		 	return;
		 } // protected function displayImage(event:Event):void
 
 
		 protected function cannotDisplayImage(event:FaultEvent):void
		 {
		 	Alert.show(event.toString());
		 } // protected function cannotDisplayImage(event:FaultEvent):void
 
	]]>
</fx:Script>
 
	<s:HGroup width="100%" height="100%">
		<mx:Spacer />
 
		<s:VGroup width="100%" height="100%">
			<mx:Spacer />
 
 
			<s:HGroup width="100%">
				<mx:Label text="Height"/>
				<mx:TextInput id="imageHeight" width="60" text="150" />
				<mx:Label text="Width"/>
				<mx:TextInput id="imageWidth" width="60" text="150" />
			</s:HGroup>
 
			<s:HGroup width="100%">
				<s:Label text="Quality" />
				<s:ComboBox width="50" 
							id="errorCorrectionLevel" 
							selectedIndex="1" 
							dataProvider="{qrCode.qualityArray}" />
 
 
				<s:Label text="Margin"/>
				<s:ComboBox  
					width="50"
					selectedIndex="4" 
					id="margin" 
					dataProvider="{qrCode.marginArray}"  />
			</s:HGroup>
 
			<s:VGroup gap="0" width="100%">
 
				<s:TabBar id="tabBar"  width="100%" dataProvider="{viewstack1}" />
 
				<mx:ViewStack 
					id="viewstack1" 
					width="100%" 
					height="225" >
					<s:NavigatorContent label="Text" width="100%" enabled="true" >
						<s:BorderContainer width="100%" height="100%" backgroundColor="#F0F0F0">
 
							<s:VGroup width="100%">
								<mx:Spacer  />
								<s:Label text="Enter Your Text Here" />
								<s:TextArea height="100%" width="95%" id="textToEncode" text=""/>
							</s:VGroup>
						</s:BorderContainer>
					</s:NavigatorContent>
 
					<s:NavigatorContent label="Email" width="100%" height="100%">
						<s:BorderContainer width="100%" height="100%" backgroundColor="#F0F0F0">
							<s:VGroup width="100%">
								<mx:Spacer />
								<s:Label text="Enter Your Email Here" />
								<s:TextInput width="95%" id="emailToEncode" text="mailto:" height="18"/>
							</s:VGroup>
						</s:BorderContainer>						
					</s:NavigatorContent>
 
					<s:NavigatorContent label="URL" width="100%" height="100%">
						<s:BorderContainer width="100%" height="100%" backgroundColor="#F0F0F0">
							<s:VGroup>
								<mx:Spacer />
								<s:Label text="Enter Your URL Here" />
								<s:TextInput width="95%" id="urlToEncode" text="" height="18"/>							
							</s:VGroup>
						</s:BorderContainer>						
					</s:NavigatorContent>
 
					<s:NavigatorContent label="VCard" width="100%" height="100%">
						<s:BorderContainer width="100%" height="100%" backgroundColor="#F0F0F0">
							<s:VGroup width="100%" height="100%"  gap="1" >
								<s:HGroup width="100%">
									<mx:Label text="First Name" width="75"/>
									<s:TextInput id="txtFName"/>
								</s:HGroup>
								<s:HGroup width="100%">
									<s:Label text="Last Name" width="75"/>
									<s:TextInput id="txtLName"/>
								</s:HGroup>
								<s:HGroup width="100%">
									<s:Label text="Address" width="75"/>
									<s:TextInput id="txtAddress"/>
								</s:HGroup>
								<s:HGroup width="100%">
									<s:Label text="City" width="75"/>
									<s:TextInput id="txtCity"/>
								</s:HGroup>
								<s:HGroup width="100%">
									<s:Label text="State" width="75"/>
									<s:TextInput id="txtState"/>
								</s:HGroup>
								<s:HGroup width="100%">
									<s:Label text="Postal Code" width="75"/>
									<s:TextInput id="txtPostalCode"/>
								</s:HGroup>
								<s:HGroup width="100%">
									<s:Label text="Phone" width="75"/>
									<s:TextInput id="txtPhone"/>
								</s:HGroup>
								<s:HGroup width="100%">
									<s:Label text="Email" width="75"/>
									<s:TextInput id="txtEmail"/>
								</s:HGroup>
								<s:HGroup width="100%">
									<s:Label text="URL"  width="75"/>
									<s:TextInput id="txtUrl"/>
								</s:HGroup>
							</s:VGroup>
						</s:BorderContainer>						
					</s:NavigatorContent>					
				</mx:ViewStack>
			</s:VGroup>
 
 
 
			<s:HGroup width="100%" horizontalAlign="center">				
				<s:RadioButton label="PNG" groupName="grpImageType" selected="true"/>
				<s:RadioButton label="JPG" groupName="grpImageType" />
			</s:HGroup>
			<s:Button label="Create QRCode" width="100%" id="drawImage" click="createImage()"/>
		</s:VGroup>
		<mx:Spacer />
 
	</s:HGroup>
 
</s:WindowedApplication>

Tags: actionscript, adobe, code signing, flash, flex, packaging, qr code
Posted in Flex, Programming | No Comments »

 

PHP, Community and User Groups

Friday, August 6th, 2010

Dear Reader,

Those that know me know that the PHP community is near and dear to my heart. I am always looking for ways to support it, usually to the chagrin of my employer. We have the greatest community of any programming language I know of and I like helping people find ways to participate in it.
(more…)

Tags: community, PHP, php user groups, video
Posted in PHP | 2 Comments »

 

Working with windows in Flex

Monday, August 2nd, 2010

Dear Reader,

I am putting the finishing touches on my reference application to demonstrate my wrapper for Google’s QR Code API. While doing so I decided to see what it would take to have the image pop up in a new window. Originally I had it displaying on the right side of the main window but aesthetically, that just didn’t do it for me.

“A journey of a thousand steps begins with a subclass…”

I created an ActionScript subclass of the spark Window class thinking that I would then subclass it as MXML and do the visual parts. This technique called “Code Behind” is considered a best practice when building Flex components. However, once I started working on it, I realized that I only needed 2 graphical elements. It just seemed easier to code it all in AS.

The Constructor

The main body of code in this window is in the constructor. We hand in the two pieces of information that it needs to set things up, the actual image to display and the type of image to save. In the reference application, the user is presented with 2 options for image type, PNG and JPG. It should be noted that I don’t actually validate that here although down below, if it is not a PNG then the user is getting a JPG, regardless of what they specified.

public function ImageWindow(thisImage:Image,thisImageType:String)
{
	super();
	this.showStatusBar=false;

I always turn of the status bar on windows that aren’t the main application window. Unless I know I am going to use it, I turn it off on the main application window as well. For the most part, it is just wasted screen real estate.

In this next section, we actually create the 2 graphical elements of the window, the image itself and the Save button. After we create them, we create a VGroup to position them for us. Once we have added both the image and the button to the VGroup, we add the vGroup to the Window so it and all of it’s elements will display.

/* 
 * Image
 */
imageType           = thisImageType;
qrCodeImage         = thisImage;
qrCodeImage.x       = 0;
qrCodeImage.y       = 0;
this.width          = qrCodeImage.width+10;
this.height         = qrCodeImage.height+30;
qrCodeImage.visible = true;
 
 
/* 
 * Button
 */
btnSave.label   = "Save";
btnSave.y       = thisImage.height+10;
btnSave.x       = 0;
btnSave.visible = true;
btnSave.addEventListener(MouseEvent.CLICK,imageSave);
 
 
/* 
 * Vertical Group
 */
var vg:VGroup = new VGroup();
vg.horizontalAlign="center";
vg.addElement(qrCodeImage);
vg.addElement(btnSave);
this.addElement(vg);
return;
} // public function ImageWindow(thisImage:Image,thisImageType:String)

Saving the image

This code was originally in the main application. It didn’t really belong to the QRCode.as but it never felt right in the main application either. When I created ImageWindow.as, I realized I had found it it’s home. imageSave() is called when the Save button is clicked.

The first thing it does is create a BitmapData and load the image into it. Next it determines the type of image to create. As I said earlier, even though PNG and JPG are valid options, the way the code reads, it is either PNG or you are getting a JPG. Once the image is encoded into a ByteArray and a default name is set, we use the FileReference class to save it. This will open the operating system’s native Save As dialog and allow you to change the name and place it in the proper folder.

The final thing we do here is close the window. If you have saved it, that is really all you can do in this window. So you might as well get rid of it.

protected function imageSave(event:Event):void
{
        var bmpd:BitmapData = new BitmapData(qrCodeImage.width,qrCodeImage.height);
        bmpd.draw(qrCodeImage);
 
        var imgByteArray:ByteArray = null;
        var fileName:String  = "";
 
        if (imageType=="PNG") {
                var pngenc:PNGEncoder = new PNGEncoder();
                imgByteArray = pngenc.encode(bmpd);				
                fileName = "qrCode.png";
        } else {
                var jpgenc:JPEGEncoder = new JPEGEncoder(100);
                imgByteArray = jpgenc.encode(bmpd);				
                fileName = "qrCode.jpg";
        } // if (grpImageType.selectedValue=="PNG")
 
        var file:FileReference = new FileReference();
        file.save(imgByteArray, fileName);
        this.close();			
} // protected function imageSave(event:Event):void

Using it

Ok, enough with the subclasses, lets look at how this works in an actual application.

The first thing we need is some place to store our Window. I usually do one of two things. If I know I will need more than one window, I usually create an array in the application global space. Then I can just add my windows to the array. If I know I am only going to use one, as in this case, I simply create a single variable to hold it, again in the global application space. I do not pretend that this is a best practice – the practice of storing variables in the global application space – however, for small projects such as this, it works. Regardless of how you do it, you need to make sure you have a handle to the window so that you can close it later. Otherwise, your application may not properly close and exit.

var imageWindow:ImageWindow;

When I am ready to show the graphic, it is a simple matter of instantiating the IageWindow class, passing in the image and the image type as parameters to the constructor.

Well, it would be simple if it weren’t for the fact that I am re-using the same window and to be nice, I want to make sure that if the user has moved the window off to one side, I honor their wishes and keep it there whenever I refresh it. What I end up with looks like this.

protected function displayImage(event:Event):void
{
       var thisImage:Image = event.target.getImage();
       var winX:int = -1; 
       var winY:int = -1;

Here is where we store off the current window’s X and Y coordinates. If the window doesn’t exist, this will throw and error. Since we aren’t really interested in the error, there is nothing in the catch. The final thing we do is close the existing window and null it out.

It was interesting (and by interesting I be frustrating to the point of drinking) that the window’s X and Y properties were always 0. You hae to use the X and Y coordinates of the nativeWindow property to actually find/affect the position of the window.

       try {
               winX=imageWindow.nativeWindow.x;
               winY=imageWindow.nativeWindow.y;
               imageWindow.close();
               imageWindow = null;
       } catch (e:Error) {               
       }

Now we create the new window with the new image. For those wondering, yes, I could have simply re-used the existing window each time and rebuilt the graphic. If I were doing this for anything other than demonstration purposes, I would have. However, I wanted to show positioning windows in this post so I did it this way.

       imageWindow = new ImageWindow(thisImage,grpImageType.selectedValue.toString());
       if (winX>-1) {
               imageWindow.open();
               imageWindow.nativeWindow.x=winX;
               imageWindow.nativeWindow.y=winY;
       } else {
               imageWindow.open();
       }
       return;
} // protected function displayImage(event:Event):void

Conclusion

That’s it. The more I work on my reference application for my QR Code creator class, the smaller the actual application becomes. Moving things like the imageSave() into the control that actually deals with the image makes more sense to me. If you are curious, I did not put the imageSave method in QRCode.as for a couple of reasons. The main one was that imageSave uses the FileReference class; that is an AIR specific class. This would have limited QRCode to being an AIR only class. As it is, if you write a proxy for the Google API on your web site, you can use QRCode.as in a Flash app that serves up QR Codes on web pages. (I am not entirely sure why you would want to use but I didn’t want to limite your future creativity.)

Hopefully, next week, I will get around to cleaning up the reference app, publish it and be able to move onto another project. :)

Until next time,
I <3 |<
=C=

Disclosure: I work with Blue Parabola, Adobe is a customer of Blue Parabola.

ImageWIndow.as

package
{
	import flash.display.BitmapData;
	import flash.display.DisplayObject;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.net.FileReference;
	import flash.utils.ByteArray;
 
	import mx.controls.Image;
	import mx.graphics.codec.JPEGEncoder;
	import mx.graphics.codec.PNGEncoder;
 
	import spark.components.Button;
	import spark.components.VGroup;
	import spark.components.Window;
 
	public class ImageWindow extends Window
	{
		public var imageType:String = 'PNG';
		public var btnSave:Button = new Button() ;
		public var qrCodeImage:Image = new Image();
 
		public function ImageWindow(thisImage:Image,thisImageType:String)
		{
			super();
			this.showStatusBar=false;
 
			/* 
			 * Image
			 */
			imageType           = thisImageType;
			qrCodeImage         = thisImage;
			qrCodeImage.x       = 0;
			qrCodeImage.y       = 0;
			this.width          = qrCodeImage.width+10;
			this.height         = qrCodeImage.height+30;
			qrCodeImage.visible = true;
 
 
			/* 
			 * Button
			 */
			btnSave.label   = "Save";
			btnSave.y       = thisImage.height+10;
			btnSave.x       = 0;
			btnSave.visible = true;
			btnSave.addEventListener(MouseEvent.CLICK,imageSave);
 
 
			/* 
		 	 * Vertical Group
			 */
			var vg:VGroup = new VGroup();
			vg.horizontalAlign="center";
			vg.addElement(qrCodeImage);
			vg.addElement(btnSave);
			this.addElement(vg);
			return;
		} // public function ImageWindow(thisImage:Image,thisImageType:String)
 
 
		protected function imageSave(event:Event):void
		{
			var bmpd:BitmapData = new BitmapData(qrCodeImage.width,qrCodeImage.height);
			bmpd.draw(qrCodeImage);
 
			var imgByteArray:ByteArray = null;
			var fileName:String  = "";
 
			if (imageType=="PNG") {
				var pngenc:PNGEncoder = new PNGEncoder();
				imgByteArray = pngenc.encode(bmpd);				
				fileName = "qrCode.png";
			} else {
				var jpgenc:JPEGEncoder = new JPEGEncoder(100);
				imgByteArray = jpgenc.encode(bmpd);				
				fileName = "qrCode.jpg";
			} // if (grpImageType.selectedValue=="PNG")
 
			var file:FileReference = new FileReference();
			file.save(imgByteArray, fileName);
			this.close();			
		} // protected function imageSave(event:Event):void
	} // public class ImageWindow extends Window
} // package

Tags: adobe, flex, qr code, windows
Posted in Flex, Programming | Comments Off

 

Actionscript class to create QR Codes

Monday, July 26th, 2010

Dear Reader,

Anyone that knows me knows that I love new toys. Sometimes it’s a new piece of hardware or a new game. Sometimes however, it is just a idea to play with. This post is all about an idea. I’ve been intrigued by QR Codes for a while. Actually, my love of bar codes goes way back to when I used to run computer systems at my parent’s company. Even back then I saw how they could change things. These days, Bar Codes are ubiquitous in retail but haven’t moved out of the business niche. QR Codes stands ready to change that. Given that most smart phones can translate them they make a great way to link the real world to the net.

There are great resources on the web to generate QR Codes and save them to your computer for later use. However, being a developers, I wanted something I could play with and incorporate into my own applications. The problem is that generating a QR Code is not a trivial thing. Lucky for us however, Google has done the hard part already and all I really need to do is wrap their API in a class and I can use this in my own applications.

What you need to get started

If you want to create QR Codes, you have to be able to do two things, create them and read them. Since this post is all about creating them, I will leave the reading them part to you. If you have a smart phone, there is probably a good application for you to use to verify your work. For the Android OS, I use BarCode Scanner.

That’s it. All you need is a way to check your work. Google does the heavy lifting of creating the graphic and the code below gives you a nice easy wrapper to allow you to include it in your Flash Builder 4 applications.

The fun part

Ok, let’s take apart the code. This is one of my first Flex components that I am confident enough to release. That having been said, I do welcome any comments or criticisms about the class.

If you haven’t yet, go review Google’s Chart API page on QR Codes. Google has boiled it down to the essentials, 3 required parameters and two optional. The heart of the class puts those parameters together and makes the call to the API. It takes the image it gets back and puts it in a form that Flex applications can re-purpose.

Planning for all EVENTualities

[Event(name="imageCreated", type="ImageCreatedEvent")]
[Event(name="imageFailed",  type="ImageFailedEvent")]

First we have to prepare for the events that can happen. In our case, either it works or it doesn’t. Either way, we need to provide the developer using this class with a way to handle both eventualities in their code so we don’t have to in our code.

The code for the events is located below, after the main class. The ImageFailedEvent is just a subclass of FaultEvent and serves only as a marker that can be trapped separately.

ImageCreatedEvent however, has an additional property of thisImage. When the even is fired, it passes a copy of the QRCode object to all listeners allowing them to easily fetch and incorporate the resulting graphic into the application.

The BINDABLEs

[Bindable] public var qualityArray:ArrayCollection = new ArrayCollection(["L","M","Q","H"]);
[Bindable] public var marginArray:ArrayCollection  =  new ArrayCollection([1,2,3,4,10,40]);

Of all the properties we have, only two are bindable, the two data arrays that specify the acceptable parameters for Error Correction and Margin.

All other parameters have reasonable defaults but in using the class, no reason to actually make them bindable presented itself.

The heart of the matter

var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, imageComplete);
loader.load(request);

The heart of the class is the createImage() method. To use the class, you instantiate the class, set the required properties, and then call createImage(). Within that method, the first thing we do is a parameter check. Make sure we have given the image a valid height and width and that we actually have something to encode. Once we can make a reasonable call to the API, we setyp for the call, make it and deal with the results.

[Side Note: QR Codes can store a variety of information. The one above is simple text. Anything that can be represented as text can be encoded. The zxing library can interpret several different types of data and the Android app will allow you to act on it appropriately. If the content starts with mailto: then the app asks me f I want to send an email to this person. If it starts with http: then the app asks me if I want to open it in a browser. Similarly, if it starts with BEGIN VCARD: then it will let me store the resulting VCARD in my contacts. Your application will most likely support similar features. In the reference application I created to test QRCode.as, I allow for encoding text, emails, URLs and VCARDS. However, this is ancillary to the QR Code specification. The specification does not dictate formats, only maximums for the different types of data (numeric, alphanumeric, binary, etc.) being stored.]

Back in February I wrote a post about Dynamically loading images from the web. In it, I talked about how calling an API that does not return XML or JSON is not as straightforward as it might be in Flex. I used the techniques (and probably the code) that I displayed in that post to make the call to Google’s chart API. I won’t rehash the previous post, you can review it as your leisure. At it’s core is a call to import flash.display.Loader to load the contents from the web.

createImage() does have one new feature, debug. If you set debug = true you will get an alert showing you the parameters being passed to Google’s API. I am a big believer in giving developers a way to see what is going on without having to hack the code. If you have Charles installed and running, you can see the call actually being made, however, I find it easier just to incorporate debugging into the code and allow developers the option. The dark side of this is that developers have the option of shooting themselves in the food this way as well. More than one developer has left debugging turned on when releasing their code.

Dealing with the aftermath

Since Flex is asynchronous, we have to have a method to deal with the image once the API call is done. imageComplete() is the handler for the COMPLETE event. In it, we set the height and width of the actual image as well as the data from the image itself. Then we dispatch the imageComplete event so that the main program can do something with the image.

Wrapping it up

That is it for the class. To use it, just instantiate, set the parameters and fire createImage() Of couse because we are talking to an API at Google, this only works with AIR applications. If you need a Flash application that runs from your own domain to be able to use QRCode.as, you will have to setup a project on your server to proxy the call to Google. (Trivial but beyond the scope of what we are discussing here.)

Until next time,
I <3 |<
=C=

Disclosure: I work with Blue Parabola, Adobe is a customer of Blue Parabola.

The Code

/*
 * Instantiate the object
 */
var qrCode:QRCode = new QRCode();
 
/*
 * Add our event listeners
 */
qrCode.addEventListener('imageCreated',this.displayImage);
qrCode.addEventListener('imageFailed',this.cannotDisplayImage);
 
 
/*
 * Setup for the call
 */
qrCode.width                = parseInt(imageWidth.text);
qrCode.height               = parseInt(imageHeight.text);
qrCode.margin               = margin.selectedIndex;
qrCode.errorCorrectionLevel = errorCorrectionLevel.selectedIndex;
qrCode.content              = contentToEncode; 			
qrCode.imageType            = grpImageType.selectedValue.toString();
 
/*
 * make the call
 */
qrCode.createImage();
/**
 * 
 * http://code.google.com/apis/chart/docs/gallery/qr_codes.html
 */
package
{
	[Event(name="imageCreated", type="ImageCreatedEvent")]
	[Event(name="imageFailed",  type="ImageFailedEvent")]
 
	public class QRCode
	{
		import flash.display.BitmapData;
		import flash.display.Loader;
		import flash.display.LoaderInfo;
		import flash.net.FileReference;
		import flash.net.URLRequest;
		import flash.net.URLVariables;
		import flash.utils.ByteArray;
 
		import mx.collections.ArrayCollection;
		import mx.controls.Alert;
		import mx.controls.Image;
		import mx.graphics.codec.JPEGEncoder;
		import mx.graphics.codec.PNGEncoder;
		import mx.rpc.events.FaultEvent;
		import mx.rpc.events.ResultEvent;
 
 
		[Bindable] public var qualityArray:ArrayCollection = new ArrayCollection(["L","M","Q","H"]);
		[Bindable] public var marginArray:ArrayCollection  =  new ArrayCollection([1,2,3,4,10,40]);		
 
		public var height:int = 150;
		public var width:int  = 150;
 
		public var content:String           = "";		
		public var debug:Boolean            = false;		
		public var errorCorrectionLevel:int = 0;
		public var imageType:String         = 'PNG';
 
		public var margin:int               = 0;
 
		protected var _image:Image = new Image();
		protected var apiUrl:String         = 'http://chart.apis.google.com/chart';
 
		public function QRCode()
		{
			/*
			* cht=qr
			* chs=<width>x<height>
			* chl=<data>
			* chd
			* choe=<output_encoding>
			* chld=<error_correction_level>|<margin>
			* 
			* Notes:
			* changing the quality needs to make sure the max number of chs hasn't been exceeded.
			* Add Types
			*/
		}
 
 
 
		public function createImage():void
		{
			// do a property check and throw appropriate error for missing properties like content to encode
			if (this.width<1) {
				dispatchEvent(new ImageFailedEvent(ImageFailedEvent.IMAGE_FAILED,false,false));
				return;
			}
			if (this.height<1) {
				dispatchEvent(new ImageFailedEvent(ImageFailedEvent.IMAGE_FAILED,false,false));
				return;
			}
			if (this.content.length<1) {
				dispatchEvent(new ImageFailedEvent(ImageFailedEvent.IMAGE_FAILED,false,false));
				return;
			}
 
			var params:URLVariables = new URLVariables();
			params.cht  = 'qr';
			params.chs  = this.height +'x'+ this.width;
			params.chl  = this.content;
			params.chld = qualityArray[errorCorrectionLevel]+"|"+marginArray[margin];
 
			var request:URLRequest = new URLRequest(apiUrl);
			request.method="POST";
			request.data = params;
 
			// DEBUGGING
			if (this.debug) {
				var a:String = apiUrl + 
					'?cht='+params.cht
					+"&chs="+params.chs
					+"&chld="+params.chld
					+"&chl="+params.chl
				Alert.show(a);			                                                       
			} // if (this.debug)
 
			var loader:Loader = new Loader();
			loader.contentLoaderInfo.addEventListener(Event.COMPLETE, imageComplete);		 	
			loader.load(request);
		} // protected function createImage():void
 
 
		protected function imageComplete(event:Event):void
		{
			var loader:Loader = (event.target as LoaderInfo).loader;
			_image.data   = loader.content;
			_image.width  = loader.width;
			_image.height = loader.height;
			dispatchEvent(new ImageCreatedEvent(ImageCreatedEvent.IMAGE_CREATED,false,false,_image)); 
			return;
		} // protected function displayImage(event:Event):void
 
		protected function cannotDisplayImage(event:FaultEvent):void
		{
			dispatchEvent(new ImageFailedEvent(ImageFailedEvent.IMAGE_FAILED,false,false)); 
		} // protected function cannotDisplayImage(event:FaultEvent):void
 
		public function getImage():Image{
			return this._image;
		}
 
	}
}

ImageCreatedEvent.as

package 
{
	import flash.events.Event;
	import mx.controls.Image;
 
	public class ImageCreatedEvent extends Event
	{
		public static const IMAGE_CREATED:String = 'imageCreated';
		protected var _image:Image;
 
		public function ImageCreatedEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false, thisImage:Image=null)
		{
			super(type, bubbles, cancelable);
			_image = thisImage;
			return;
		}
 
		override public function clone():Event 
		{
			return new ImageCreatedEvent(type, bubbles, cancelable, _image);
		}
 
	}
}

ImageFailedEvent.as

package
{
	import mx.rpc.events.FaultEvent;
 
	public class ImageFailedEvent extends FaultEvent
	{
		public static const IMAGE_FAILED:String = 'imageFailed';
 
		public function ImageFailedEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false)
		{
			super(type, bubbles, cancelable);
		}
 
	}
}
Disclosure: I work with Blue Parabola. Adobe is a customer of Blue Parabola.

Tags: adobe, flash, flex, qr code
Posted in Flex, Programming | 9 Comments »

 

Migrating Flex components to Flex Libraries

Wednesday, July 21st, 2010

Dear Reader,

Recently I decided to consolidate all of the custom and reusable controls I had created in Flex into a single library. Even as good as Eclipse is at refactoring (one of the things it actually does well) it still wasn’t was clear cut of a procedure as I had hoped for.
(more…)

Tags: adobe, flex, libraries
Posted in Flex, Programming | Comments Off

 

Custom Events vs. Callbacks in ActionScript

Monday, June 21st, 2010

Dear Reader,

Having just completed an article on Callbacks in ActionScript and having previously written about custom events in ActionScript I thought I would take a few minutes and contemplate on the difference between the two and when I see each being useful. Bear in mind that I still have only a small amount of experience in Flex and ActionScript. This is more of a straw man post than a definitive guide and I welcome thoughtful criticisms and comments.

Both callbacks and events allow us to respond to events, the difference is how and to an extent, how complex. Custom events are a bit more complex to code and maybe that is why I see callbacks as more useful on a day to day basis.
(more…)

Tags: actionscript, best practices, callbacks, custom events, flex, object oriented programming
Posted in Flex, Programming | Comments Off

 

DPC Wrap-up

Monday, June 14th, 2010

Dear Reader,

I’m sitting in Schipol airport with Rob Allen waiting for my flight. (Rob is reviewing his photos) Thinking back over the past few days, the word that comes to mind is fantastic. DPC10 was fantastic.

For those that don’t know me, I have a personal interest in DPC as last year I worked at Ibuildings and hosted it. It’s always difficult to hand off something that means so much to you but I am happy to report that Lorna Jane took it and made it her own. It was everything I had hoped it would be.

(more…)

Tags: adobe, Amsterdam, chris shiflett, dutch php conference, elizabeth naramore, glee, lorna jane mitchell, matthew, rob allen
Posted in PHP | Comments Off

 

Flash Builder 4 Data Wizard for HTTP Services – A love story in 1 act

Monday, June 7th, 2010

Dear Reader,

Disclosure: I work with Blue Parabola, Adobe is a customer of Blue Parabola.

If you’ve been paying attention, you will have noticed that I recently published a series of articles over on phparch.com discussing the new Data Connection Wizard for PHP that Adobe has built into Flash Builder 4. While I am not a great fan of Wizards in general, I recognize that there is a segment of the developer population that does like them so I respect that and try not to run screaming from the room every time I see someone using one.
(more…)

Tags: actionscript, adobe, flash builder 4, HTTP
Posted in Flex, Programming | 1 Comment »

 

Flash Builder 4 and packaging

Wednesday, June 2nd, 2010

Dear Reader,

I am currently teaching a class for php|architect and Adobe, “Rich Internet Applications with Flex and PHP“. Today, one of the topics we covered was packaging classes. I was demonstrating how easy it was to create packages in Flash Builder 4 and going along nicely using my slides when a student asked a question.

“Why doesn’t it actually create the sub packages for me? Do I need to do that myself?”

(more…)

Tags: adobe, classes, eclipse, flash builder 4, packaging
Posted in Programming | Comments Off

 

Bing Search API wrapper for PHP

Tuesday, June 1st, 2010

Dear Reader,

Back in March I had the opportunity to work on a project for Microsoft that is now coming to light. I was tasked with writing a PHP wrapper for the Bing API that Microsoft has been building. I’ve written API wrappers before and it is either a fun experience or it is a nightmare, depending on whether the API is easy to work with. I am pleased to report that the Bing Search API is well thought out and very easy to work with. For PHP developers, I am hoping that what I am about to show you makes it even easier.
(more…)

Tags: API, bing, microsoft, wrapper
Posted in PHP | 21 Comments »

 

Seeing Microsoft in the big picture

Tuesday, May 25th, 2010

Dear Reader,

Disclosure: I work with Blue Parabola, Microsoft is a customer of Blue Parabola. Blue Parabola also puts on the TEK series of conferences.

It seems like every six months or so I write a pro-Microsoft post. It’s not really on purpose, it just seems like two to three times a year they remind me that there are pools of brilliance in that company, even if they are surrounded by oceans of stupidity.

This post’s catalyst

I have a lot of contact with Microsoft due to my job. However, most of it is at a detail level where it is easy to lose the big picture. It wasn’t until TEK·X and the session Tips & Tricks to get the most of PHP with IIS, Windows, and the Windows Azure Cloud by
Sumit Chawla & Kanwaljeet Singla that I got a glimpse of the big picture again. I began to see all of Microsoft’s efforts together as one, instead of seeing them as a series of individual efforts.

Side Note: If I had paid attention to Ruslan Yakushev’s webcast, “Making PHP faster on IIS“, I probably would have written this post earlier.

(more…)

Tags: fastcgi, linux, microsoft, tekx, wincache
Posted in PHP | 12 Comments »

 

Only YOU can prevent web failures

Friday, April 30th, 2010

Dear Reader,

[NOTE: This post is not for developers, although I would encourage developers to read it. This post is for my friends who are business owners.]

The problem

Last night I wrote the info@ email address on a website and asked a questions about their product. They politely responded with two links to their website. This morning however, when I went to visit them, I was greeted with the message above. It’s now three hours later and the site is still “experiencing difficulties”. (that is developer speak for DOWN) I started poking around to see who built the site. They are a local company and I assumed their vendor was also. A quick whois showed that their site was built/hosted, not by a local development shop where they had a personal relationship with the development team, but by a company that specialized in their particular “vertical”. Checking the vendor’s website, it was up but all of their “featured sites” were throwing the same error. Obviously they had rolled an update of their system into production and it was broken. This problem can be prevented by business owners insisting on – and paying for – “Best Practices” in their software vendors.
(more…)

Tags: regression testing, software deployment, staging, unit tests
Posted in Programming | 1 Comment »

 
« Older Entries
  • Event Registration Online for Day Camp 4 Developers : Soft Skills

  • Team Based PHP Training

  • Tags

    adobe API article Cal Evans codeworks community conference cw09 Derick Rethans developers devzone elizabeth naramore Exim flex fun IBuildings Kathy Evans linkedin Management Marketing microsoft MySQL Nashville open source phar PHP phparchitect php developers podcampnashville podcast Programming Quickies respect Sebastian Bergmann Silly-Con Valley sixty second tech software development terry chay twitter upgrade video windows wordpress zend zend framework

  • RSS PHP Podcasts

    • JSClasses, JSMag, PHP Alpha 1, PHP strict typing, IndieConf – Lately in PHP podcast episode 4
    • SitePoint Podcast #76: Wicked WordPress Themes with Allan Cole and Jeffrey Way
    • DPCRadio: Designing for Reusability
    • SitePoint Podcast #75: Awesome Overkill
    • DPCRadio: Technical Debt
    • SitePoint Podcast #74: WordPress Themes with Nathan Rice and Cory Miller
    • SitePoint Podcast #73: Cease and Desoup
    • DPCRadio: APC & Memcache the High Performance Duo
    • APC & Memcache the High Performance Duo
    • SitePoint Podcast #72: Web Video and Social Media with Gregory Ng and Wayne Sutton

  • Me

    • Best web design company
    • Cal Evans Dot Com
    • Cheap Wine Diary
    • Cyrano’s Apprentice
    • Evans Internet Construction Company
    • My Life as a Child
    • PHP Podcasts
    • Sixty Second Tech

  • Categories

    • Apache
    • BlogBling
    • Blogging
    • codeworks
    • Entertainment
    • Entrepreneurship
    • Flex
    • Humor
    • JavaScript
    • Long Form
    • Management
    • Marketing
    • Me
    • PHP
    • podcasting
    • Programming
    • SQL
    • Technology
    • Web 2.0
    • wordpress
    • WordPress Plugins
    • writing
    • zend framework


Postcards From My Life is proudly powered by WordPress
Entries (RSS) and Comments (RSS).