Smarty base href Modifier

It appears that Safari 5 will not respect the base href tag when used in a page served via https when the base href indicates a URL with the http protocol. There is probably more to this issue, but I ran across it while working on an Authorize.net integration using their SIM API. After a successful transaction, Authorize.net requests a URL of the developer's choosing, and proxies the response through their server to the user's browser. This allows developers to do callback processing of the successful transaction on their server, and display a custom "thank you" page. Brilliant. The problem is, if you have any included CSS, JavaScript, or images in the page, you either need to give them all fully qualified URLs, or use the base tag to specify a root URL to use for all relative link attributes in the page (href, src, action, etc.). I've done this same type of integration many times, going back about six years, and have never had a problem using the base href in this scenario. Now, Safari 5 will completely ignore the base href and display a big mess of un-styled markup when you reach the end of the Authorize.net transaction flow.

I do a lot of work with the Smarty template engine, and the site I'm working on now is large, complex, and has a fairly advanced layout with lots of CSS and jQuery goodies. Everything is so abstracted, it's just not practical to construct a single template for my Authorize.net relay pages, of which there are several. It's also not practical to change the templates to use fully qualified URLs for everything. So what I did was write a simple Smarty modifier that prepends a specified URL to all of the relative src, href, and action attributes within the supplied string. It also fixes background: url() inline CSS styles. On my relay templates, all I have to do is wrap the contents of the template in a {capture} function, assign the output to a variable, and run it through my modifier. Absolute paths for all. Here's the modifier code, hopefully it will save someone a few grey hairs.

<?php
/**
 * Smarty plugin
 * @package Smarty
 * @subpackage plugins
 */

/**
 * Smarty base_href modifier plugin
 *
 * Type:     modifier<br>
 * Name:     base_href<br>
 * Purpose:  apply a base URL to all relative
 *				src, href, and background:url() attributes in a string
 * @author   Rob Ruchte <rob care of thirdpartylabs com>
 * @param string
 * @param string
 * @return string
 */
function smarty_modifier_base_href($string, $base_href)
{
	$base_href = (substr($base_href, -1)!='/') ? $base_href.'/':$base_href;

	$patterns = array();
	$patterns[] = '/href=\"[^#]\/?([^\"]*)\"/i';
	$patterns[] = '/src=\"\/?([^\"]*)\"/i';
	$patterns[] = '/action=\"\/?([^\"]*)\"/i';
	$patterns[] = '/background: ?url\(\'?\/?([^\'|\)]*)\'?\)/i';
	$patterns[] = '/background-image: ?url\(\'?\/?([^\'|\)]*)\'?\)/i';

	$replacements = array();
	$replacements[] = 'href="'.$base_href.'$1"';
	$replacements[] = 'src="'.$base_href.'$1"';
	$replacements[] = 'action="'.$base_href.'$1"';
	$replacements[] = 'background: url(\''.$base_href.'$1\')';
	$replacements[] = 'background-image: url(\''.$base_href.'$1\')';

    return preg_replace($patterns, $replacements, $string);
}

/* vim: set expandtab: */

?>

Usage:

{capture assign=output}
{include file="include/header.tpl" title="Event Ticket Registration"}
	<div class="span-3" id="leftbar">
		[...]
	</div>
	<div class="span-11 last" id="content">
		<div style="background: url(/images/feature.jpg);">
			[...]
		</div>
		<div class="span-11 last" id="copy">
		<div id="breadcrumbs">
			{foreach from=$breadcrumbs item=currNav name=breadcrumbs}
				{if !$smarty.foreach.breadcrumbs.last}<a href="{$currNav.path}">{/if}
				{$currNav.title}
				{if !$smarty.foreach.breadcrumbs.last}</a>&nbsp;&raquo;&nbsp;{/if}
			{/foreach}
		</div>
        <h2>Registration: {$event->getTitle()|escape}</h2>
	    {$purchaseSuccess}
        </div>
    </div>
{include file="include/footer.tpl"}
{/capture}
{$output|base_href:$smarty.const.BASE_URL}

,

Serving a Flash Socket Policy File From Processing

Last night I spent way too long trying to get AS3 to communicate with a simple socket server I wrote in Processing. I've done this kind of thing before and seemed to recall that it was pretty simple. But in the meantime, Adobe, in an effort to be more secure, has changed the Flash player to require a "socket policy file". The socket policy file is very similar to the familiar crossdomain.xml file that defines security permissions for HTTP access. Unfortunately, the socket policy file must either be sent on demand from the sockets that the player is attempting to access, or from port 843 on the host that the player is attempting to connect to. If all you want to do is run a quick and dirty socket server and have Flash clients connect to it, this is all a huge PITA.

Since ports below 1024 require root permissions in order for processes to use them on OS X, and I didn't want to run some other server process just to serve policy files, I needed to kludge a way to send the policy file from my nice, elegant socket server every time a client connected and requested a policy file. The example below is a simplified version, as it only listens on one port - the project I was working on was basically a proxy from one SWF to another, so I had two socket servers that needed to listen for the requests for a policy file and respond. In this example, the server listens for connections on port 5208, and simply echoes incoming data from the client to System.out, and has some visual feedback in the window. When the incoming message contains the string "policy-file-request" (the entire message from Flash will be terminated with a null char), we simply spit back the XML for a wide-open socket policy, followed by a null char (This is required, or the Flash player will not accept the policy file. This is what tied me in knots last night. RTFM!)

import processing.net.*;

int bgLevel = 0;
int port = 5208;
Server server;

String flashDomainPolicy = "<?xml version=\"1.0\"?>"
                              +"<cross-domain-policy xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"http://www.adobe.com/xml/schemas/PolicyFileSocket.xsd\">"
                              +"<allow-access-from domain=\"*\" to-ports=\"*\" secure=\"false\" />"
                              +"<site-control permitted-cross-domain-policies=\"all\" />"
                              +"</cross-domain-policy>";

void setup ()
{
  size(200, 200);
  server = new Server(this, port);
  background(bgLevel);
}

void draw ()
{
  Client client = server.available();

  if (client !=null)
  {
    String message = trim(client.readString());

    if (message != null)
    {
      if (match(message,"policy-file-request") != null)
      {
        sendFlashPolicy(server);
      }
      else
      {
        server.write(message);
        System.out.println(message);

        // Change the background color to indicate message activity
        bgLevel = 255;
      }
    }
  }

  // Fade to black
  if (bgLevel>1)
  {
    bgLevel-=2;
    background(bgLevel);
  }
}

void sendFlashPolicy(Server socketServer)
{
    socketServer.write(flashDomainPolicy+char(0));
    System.out.println("Sending Flash policy file");
}

You can test this without a Flash client by firing up the sketch and telnetting to the port. Any strings you send will be echoed to System.out, unless you send the string "policy-file-request" in your message, which will result in the server sending the policy XML to you. You should be able to connect a Flash client to this server and start communicating right away.

, , , ,

Recently, Blogger began appending a tracking gif to the content of each entry in their Atom feeds. The URL used in the image src uses https, most likely to avoid warnings when it's rendered in a https context. For some reason, when rendering the feed content containing the tracking image, the Flash player can crash, taking the browser with it on certain platform/browser combinations. We found the problem in FireFox 3.0 on OSX, but only on PPC Macs. Go figure.

In our case, we are proxying the Atom feed through a PHP script so we can display the feed contents to user agents without the Flash player. This made it fairly easy to iterate through the entries, and with a simple bit of regex, strip out the offending markup from the contents.

Blogger is wrapping the image tag in a div with a very specific CSS class, which makes our job easy:

foreach($feed->entries as $currEntry)
{
    
$currEntry->content ereg_replace('<div class=\"blogger-post-footer\">.*</div>'''$currEntry->content);
}

Depending on what you're using to parse the feed, you may or may not need to be concerned about decoding and encoding html entities during this process.

, ,

Automatic AMF class mapping in Zend (Part 2)

see Part 1

Polishing off automatic mapping of objects to the correct class from PHP (Zend) back to ActionScript turned out to be easier than I thought.

In addition to the more commonly used $_explicitType class property, Zend also supports using a function called getASClassName. You can either paste this into each of your remote class stubs, or modify the getClassName method in Serializer to do it automatically.

public function getASClassName() 
{
 return ">" . str_replace("_", ".", get_class($this)); 
}

Clearly you'll need to avoid underscores appearing package and class names. That may be true regardless if you're planning to take advantage of Zend's automatic class loading.

On the Flash side, if you're not using the Flex compiler, you can use this to automatically register class aliases to the correct string.

function autoRegisterClassAlias (localClass:Class):void
{
registerClassAlias(">" + flash.utils.describeType(localClass).@name.split("::").join("."), localClass)
}

I've made it compatible with the Flex version. If the greater-than character is the cause of annoyance and confusion, feel free to remove it, but you'll need to do so on the PHP classes as well.

I'm sure this won't be our last post on this topic. If you've got lots of experience with AMF and Zend (I honestly don't) please post your thoughts in the comments.

Automatic AMF class mapping in Zend (Part 1)

First off, much thanks to Lee Coltrane for figuring this out. He's a bit busy at the moment, so I'm writing this up without the benefit of his input which I'm sure will be extremely valuable once our schedules mesh up.

A few caveats right off the bat. 1) We're still figuring out our best practices. 2) This is Flex only (see below)  3) We haven't (yet) written any code to automatically populate the $_explicitType variable going back to Flash from PHP.

If you're reading this, you've more than likely come across a Flex metatag like the one below:

[RemoteClass(alias="com.domain.FooBar")]

Personally, I found it annoying to have to populate the alias manually. The compiler knows the package name and the class name. Don't make me type it again! And, in fact, the compiler will accept the metatag without it:

[RemoteClass]

The only problem is getting Zend (in our case) to map it to the correct remote class. The Adobe engineers saw fit to prepend a greater-than character to the outgoing classname when using this syntax. So, if you omit the alias, ">com.domain.FooBar" will be sent instead.

To get ZendAmf to map this automatically, you'll need to make a slight modification to the Deserializer.php class. Mine is located in Zend/Amf/Parse/Amf3/Deserializer.php (Note: I haven't even looked at the Amf0 class yet, but my understanding is that custom class mapping isn't supported in AMF0) Insert the following code around line 315 in the readObject method after it has tried to determine the className but before it has assigned a return object.

// Allow and map automatic aliases via RemoteClass metatag & flex
if (strpos ($className, '>') === 0)
{
  $className = substr ($className, 1);
}
//translation: If the classname begins with '>' use the rest of the string instead.

This addition will deal with incoming class mapping. If you want to remove the alias parameter from all of your ActionScript RemoteClass metatags, you'll need to format your $_explicitType entries like this:

$_explicitType = ">com.domain.FooBar";

...which is what we'll look at in Part 2! 

NOTE: I said at the very beginning this is Flex only. Compiling with the Flash IDE does not support the RemoteClass metaTag, so you'll need to use registerRemoteClass as you've probably read about elsewhere. Perhaps in part 2 (or 3?) I'll write up an ActionScript class that can register classes against the correct package names automatically. Until then, I'll give you a hint: flash.utils.describeType.

Smarty File Size Modifier

Here's a simple Smarty modifier that will format an integer that represents the number of bytes in a file as a human readable string.

Usage:
{$fileSizeInBytes|file_size}

Example:
{assign var=fileSizeInBytes value=10485760}
{$fileSizeInBytes|file_size}

{assign var=fileSizeInBytes value= 768000}
{$fileSizeInBytes|file_size}

{assign var=fileSizeInBytes value=303}
{$fileSizeInBytes|file_size}

Output:
10 MB
750 Kb
303 bytes

<?php
/**
 * Smarty plugin
 * @package Smarty
 * @subpackage plugins
 */

/**
 * Smarty file_size modifier plugin
 *
 * Type:     modifier<br>
 * Name:     file_size<br>
 * Purpose:  format file size represented in bytes into a human readable string<br>
 * Input:<br>
 *         - bytes: input bytes integer
 * @author   Rob Ruchte <rob at thirdpartylabs dot com>

 * @param integer
 * @return string
 */
function smarty_modifier_file_size($bytes=0)
{
    $mb = 1024*1024;

    if ($bytes > $mb)
    {
        $output = sprintf ("%01.2f",$bytes/$mb) . " MB";
    }
    elseif ( $bytes >= 1024 )
    {
        $output = sprintf ("%01.0f",$bytes/1024) . " Kb";
    }
    else
    {
        $output = $bytes . " bytes";
    }

    return $output;
}

/* vim: set expandtab: */

?>

,

SEO Presentation for NCSU Web Developer Group

Jon and I delivered a presentation about SEO to the NCSU Web Developer Group this afternoon. If you were in attendance, thanks very much for coming to see us, we hope you found it informative. We talked about a lot of useful online resources during the presentation, all of the links are in the slide deck that we've posted for your convenience. You can download a PDF of the slides here, or view them on SlideShare

Please feel free to post comments and questions here.

I decided I'd try an FLA-less project using embedded library assets from swfs, but that proved contrary to my workflow. If I'm going to use the IDE for layout, then I've got a good handful of layers. Not just a graphic or three. I want several things to align, you know visually, so I used a visual layout tool to configure my layout. Smart, huh? But you can't pull in an entire timeline worth of assets and then provide the class and still expect to get the whole thing wired up. Flex compiler's gotta be all one child asset at a time within the class.

Despite reading more than once that you could somehow:

[Embed (src="library.swf", symbol="layout"]
public class layoutClass extends Sprite
{
// rest of class here
}

I never got it to work. I only got the directive to work for class property members, not the class definition itself.

You could use composition to pull an entire layout's worth of assets in. But then you've got this magic asset container property that's different from the local "this" context. No, I wanted all the assets linked to the class in one place, like I was used to with the IDE in the first place.

I decided it was worth it to reparent all the assets out of a temporary holder, and reconstruct the layer stack in the new parent.

I knew the code for this wasn't hard or long, but it isn't exactly obvious:

//Layout is a class that can be populated via
//embed directive or by having a 'skin' swc in your classpath. 

var layout:Sprite = new Layout()

var len = layout.numChildren
for (var i=0; i<len ; i++) {
  //move the child into this
  var c = addChild (layout.getChildAt(0))
  //link the property name of the class to the asset
  this[c.name] = c
}

Merry WhateverDecemberweenNewYear.

PNG sequence to SWF

I needed to stitch a stack of PNG images into a SWF using a server process. There are quite a few tools and methods for doing this interactively, but it gets painful if you want an unattended process.

I found SWFTools (includes PNG2SWF.EXE), which handles the awful, complex bit of ... building a SWF from a bunch of PNGs, but then I needed to figure out how to get it to auto-magically use all the PNGs in a specific folder. Essentially, I found a tool that does the hard part, but I needed to cobble together the actual command it should use. Wince if you want to, but I'm a masochist and I hacked together this batch file (like it's 1988, baby):

@echo OFF
SET LIST=

REM Build a list of PNGs
for /f "delims=" %%a in ('dir /b /a-d %1*.png 2^>NUL') do call :process %%a %1

@echo ON
REM now make a SWF!
CALL png2swf -r 20 -o %2 %LIST%

SET LIST=
goto :eof

:process
REM append the next file to the LIST variable
if not "%LIST%"=="" set LIST=%LIST%
set LIST=%LIST%%~2%~1

example usage -- test.bat c:\path\pngFolder c:\path\resulting.swf

I'm sure this is dead easy in linux (you can get swftools in linux flavors too, including source), and I'll send cookies to whoever posts the first working script in the comments.

Note: I'm not sure why this is, but the resulting SWF can't be imported into Flash CS3 as one would expect. If you were going to do that, you could've imported the initial sequence in the first place.

(see the original Mangled Kerning post)

We decided when we started this blog it wasn't going to be a bunch of whine, whine, whine such-and-such doesn't work. If we couldn't find a workaround of some sort, then we were just polluting the search space with more of the same "anyone else have an answer?" You may have dug through some of that to get here. There's value in seeing you're not the only one out there, but it's so limited we decided to avoid it if at all possible.

So. You've got an autosized textfield. You want to use Anti-alias for readability. But you've noticed strange spacing problems around HTML anchors. You've got a few options. I've come back to edit this post several times after seeing opportunities to achieve the same fix with less effort, but I'm preserving some of the more complex work-arounds because the bug is so quirky, someone might have need to try one of the uglier approaches.

1) After setting the htmlText, access the height property of the field, then it's safe to turn off autoSize which will allow correct kerning behavior:

var temp1 = (field.height)
field.autoSize = TextFieldAutoSize.NONE

You won't have to wait additional frames or anything like that. Just remember to turn autoSize back on before changing the text.

2) Set the antiAliasType property of the TextField to flash.text.AntiAliasType.ADVANCED after the field has already been on the stage for at least two (count 'em: 2) frames. That's frame 3 if you're following along at home and your TextField was present on frame 1. Use the visible property to avoid epileptic seizures.

3) It would appear that only authoring-tool-placed TextFields require the extra 2 frames. Create a TextField from code, and you can set the antiAliasType property of the TextField to flash.text.AntiAliasType.ADVANCED in the next frame. Again, use the visible property to hide the layout jump:

var field = new TextField( 20, 20, 500, 1)
addChild(field)

field.embedFonts = true
field.multiline = true
field.wordWrap = true
field.autoSize = TextFieldAutoSize.LEFT

var styles = new StyleSheet();
styles.setStyle("p", {fontFamily: "HelveticaNeueLT Std Med Cn", fontSize: "18", kerning: false, letterSpacing: 0, color: '#CCCCCC'})
styles.setStyle("a", {textDecoration: 'underline', kerning: false, letterSpacing: 0, color: '#FFFFFF'} );
field.styleSheet = styles

field.htmlText = "<p>This top line is necessary to push the content down because that matters somehow in this simple example of <a href=\"http://nowhere.com\">Quirky</a> text in Flash.</p>"

//NEXT FRAME:
field.antiAliasType = flash.text.AntiAliasType.ADVANCED

4) If you must be a sadist, use a hybrid approach to replace authoring tool-placed TextFields with runtime-created TextFields while preserving position, dimensions, autoSizing, styles, etc. And after you've gone to all the trouble, don't forget to set the antiAliasType to advanced. If you're willing to let the original field handle the autoSizing, you can turn off autoSize on the dynamic field, size it according to the first field, and avoid having to wait a frame to set the antiAliasType as in #2. Just remember that if you need to change the field's contents, you'll have to turn autoSize back on. NOTE: Your mileage may vary, but I found that wordwrap was uneffected by this bug, so even though the kerning sometimes jumps around aggressively, I didn't find any changes in wordwrapping, thus no changes in sizing to worry about ... so far.