Archive for October, 2007

Using php to dynamically generate conflict free css

This little blog has been getting a lot of coverage lately thanks to a write up by Ajaxian. Developing with Symfony is great and always gives you a lot to think and write about.

For my new product I was having a CSS conflict. This tends to happen when you include your own html and css into someone else’s website. For instance if you have a widget as such:

html

<div id="mywidget">
<h1>My hello world widget</h1>
</div>

css

H1 {
color:green;
font-size:20px;
}

The solution to this problem is quite straightforward, you simply specify your css selector as div#mywidget H1. However, what if you want to allow people to customize the looks of your widget. Now you could off course ask them to include the div#mywidget part, but chances are this will give problems.

Since I was already using the great sfCombineFilterPlugin an easy solution was available. (If you didn’t use the sfCombineFilterPlugin yet, go check it out immediately. Also have a look at Yahoo’s Yslow)

The sfCombineFilterPlugin uses php to gzip, minify and cache your css and javascript. Here is how to extend that behavior to include the #mywidget specification. (Assuming you have sfCombineFilter installed)

Step 1: open your .htaccess

Just below the RewriteBase instruction add:
# if we are retrieving javascript or css
RewriteRule ^css/packed/prepend/(.*\.css) /sfCombineFilterPlugin/combine.php?type=css&prepend=1&files=$1
RewriteRule ^css/packed/(.*\.css) /sfCombineFilterPlugin/combine.php?type=css&files=$1
RewriteRule ^js/packed/(.*\.js) /sfCombineFilterPlugin/combine.php?type=javascript&files=$1

Step 2: add this class to the top of web/sfCombineFilter/combine.php

Partly based on CSS parser class.

class prependCss
{

    public static function prependCssString($str) {
        // Remove comments
        $str = preg_replace("//*(.*)?*//Usi", "", $str);

        $parts = explode("}",$str);
        if(count($parts) > 0) {
            foreach($parts as $part) {
                list($keystr,$codestr) = explode("{",$part);
                $keys = explode(",",trim($keystr));
                $newkeys = array();
                if(count($keys) > 0) {
                    foreach($keys as $key) {
                        if(strlen($key) > 0) {
                            $key = (!strstr($key, '#mywidget')) ? '#mywidget'.$key : $key;
                            $newkeys[] = $key;
                        }
                    }
                    $keystr = implode(', ',$newkeys);
                }
                if(!empty($keystr)) //needed for spaces behind last }
                $rules[] = $keystr . " {" . $codestr . "}";
            }
            $prependedCss = implode("n", $rules);
        }
        //
        return $prependedCss;
    }

    public static function prependCssFile($filename) {
        if(file_exists($filename)) {
            return self::prependCssString(file_get_contents($filename));
        } else {
            return false;
        }
    }
}

Step 3: hack around in combine.php

below $minify_js add:
if($_GET['prepend']==1)
$prepend = true;

change the stuff below this comment to:
// Get contents of the files
$contents = '';
reset($elements);
foreach ($files as $path) {
if($prepend && $_GET['type'] == 'css') {
$contents .= "\n\n" . prependCss::prependCssFile($path);
} else {
$contents .= "\n\n" . file_get_contents($path);
}
}

And finally just change your urls to css/packed/prepend/yourcss.css (if you are using relative paths in your css you might need to add an ../)

Conclusion

Using this technique your css will load without any problems in third party sites. This comes in very useful when creating widgets or greasemonkey scripts.

Css & PHP & Symfony & Web Development tschellenbach 29 Oct 2007 13 Comments

Introducing a cross site ajax plugin for Prototype

Update: there have been some improvements to this plugin. Have a look at this post regarding the update. Thanks for the feedback!

After some days of hard labor, I finished my cross site Ajax plugin for the prototype framework 1.5.0. (Download Plugin Here) While working on a new product of mine I realized I needed cross site Ajax, which is not supported in the Prototype framework.

During cross site Ajax requests the standard XmlHttpRequest approach breaks down. The problem is that XmlHttpRequest is bounded by the same site policy. Fortunately the script tag has the freedom to do as it pleases.

Some other libraries such as dojo and jquery do support the script method for doing Ajax. There is even a project on source-forge called COWS, which is dedicated to this purpose. This plugin is an adaptation of the jquery plugin, but modeled to look like an XmlHttpRequest. The credits of the original code go to Ralf S. Engelschall , which amazingly achieved to make it nicely cross browser compatible. This plugin supports FF, IE, Safari, Opera and Konqueror, but has only been properly tested in FF and IE.

Prototype’s structured way of doing Ajax was my main reason to choose the prototype framework. Furthermore it is also included in the great Symfony framework. In Prototype Ajax requests are written like this:

new Ajax.Request('myurl', {
method: 'GET',
crossSite: true,
parameters: Form.serialize(obj),
onLoading: function() {
//things to do at the start
},
onSuccess: function(transport) {
//things to do when everything goes well
},
onFailure: function(transport) {
//things to do when we encounter a failure
}
});

The cross site plugin simply allows you to do Ajax cross site, by specifying crossSite: true (line 3 of the above example). I will now cover some technical aspects of the plugin, but if you just want to start using it simply skip to the plug and play instructions below.

How it works – Technical Aspects

This plugin uses the dynamic script tag technique. This basically means that we insert new <script> tags into the Dom. Since this script tag is not bound to the same site you can send and receive data in the Ajax way. In its most basic form the javascript would be like this:

this.node = document.createElement('SCRIPT');
this.node.type = 'text/javascript';
this.node.src = 'http://www.serversite.com';
var head = document.getElementsByTagName('HEAD')[0];
head.appendChild(this.node);

In order to make it very easy to use with Prototype, or any other library for that matter, I decided to mimic the functions of the XmlHttpRequest. This is easily achieved by implementing the functions open, send and onreadystatechange. Furthermore I needed to specify the variables readyState and status in order to support prototype’s onLoad, onSucces and onFailure.

Detecting the loading of a script element is not that easy. Browsers such as Safari and Konqueror simply give no indication of this at all. One common solution to dealing with this is to use an interval and perform a check. The work at TrainOfThoughts however takes the beautiful approach of inserting a helper script. This exploits the fact that the dynamically added scripts are executed in sequence. This approach makes the plugin nicely cross browser compatible.

Detecting failure is rather cumbersome for the script technique. As far as I know there is no way to read the headers on the incoming file, or to inspect its contents through javascript. This leaves us with the rather blunt approach of setting a global variable using the server output. It works, but it could be prettier.

Plug and Play implementation instructions

Firstly you need to load the plugin javascript file: download cross site ajax plugin for the prototype framework 1.5.0.

Secondly you need to change your regular prototype Ajax request, by ensuring that you instruct it to use the crossSite and GET methods, as such (observe line 2 and 3):

new Ajax.Request(baseurl+'/comment/giveratingjs', {
method: 'GET',
crossSite: true,
parameters: Form.serialize(obj),
onLoading: function() {
//things to do at the start
},
onSuccess: function(transport) {
//things to do when everything goes well
},
onFailure: function(transport) {
//things to do when we encounter a failure
}
});

Thirdly you might need to rewrite some of your javascript code to accommodate the instant execution of the scripts.

Fourthly, if you want to use onFailure for any of your scripts you need to send some javascript instructions back from the server. You need to do this both on success and on failure (since a global variable is used). This is the javascript variable you need to set:

'var _xsajax$transport_status = 200;' Or
'var _xsajax$transport_status = 404;'

Symfony specific tips

Symfony detects if it receives a XmlHttpRequest and automatically turns off your debug bar and layout. Unfortunately it is not so kind to the script technique. So in your action you need to do this manually:

sfConfig::set('sf_web_debug', false);
$this->setLayout(false);

Furthermore your validation files by default only look at POST variables (this one tricked me). To instruct them to look at both, simply mention

methods: [post, get]

at the top of your validation.yml

Since you will probably want to send html to the browser, I would suggest you put this little function (found in the symfony escape helpers) in your toolbox.

public static function esc_js($value) {
return addcslashes($value, "\0..\37\\'\"\177..\377\/");
}

Conclusion

The dynamic script tag technique opens up a wide range of possibilities. Personally I am very glad with the results and would like to thank Ralf S. Engelschall for his superb cbc work. Unfortunately I didn’t include an example this time. You will have to wait for the products’ launch:). Comments and improvements are always appreciated. Enjoy your cross site scripting!

Javascript & PHP & Prototype & Symfony & Web Development tschellenbach 25 Oct 2007 240 Comments

Symfony & Gravatars – easy implementation

Lets start with a small explanation. Gravatars are so called ‘globally recognized avatars’. Basically it is an open directory for avatars. If you didn’t get one yet, feel free to head over to www.gravatar.com.

The implementation of gravatars for your site is already extremely easy. However if you are fortunate enough to be using Symfony, it becomes a real piece of cake. Quite a few people already use gravatars, including the Symfony blog. This number will probably increase quite a bit, given the recent purchase of the company by Automattic.

Gravatars are attached to an email address. Lets assume your program is already setting and getting the email addresses. All you need to get up and running with Gravatars is these simple 3 steps.

1. Extend your setEmail to do setGravatar as well

(somewhere in lib/Comment.php)

function setEmail($input) {

$this->setGravatar(md5($input));

parent::setEmail($input);

}

2. When getting the Gravatar, retrieve the full image code

(somewhere in lib/Comment.php)

function getGravatar() {
$md5email = parent::getGravatar();
$size = 45;
$rating = 'R'; // possible values [ G | PG | R | X ]
$url = '<img width='.$size.'px height='.$size.'px class="gravatar" src="http://www.gravatar.com/avatar.php?gravatar_id='.$md5email.'&rating='.$rating.'&size=35" alt="gravatar" />';
return $url;
}

3. In your view template

Simply do: $comment->getGravatar();

DONE!

Have a look at the result:
Gravatar implementation

PHP & Symfony & Web Development tschellenbach 19 Oct 2007 53 Comments

We love FireFox, 76%

A few days ago I noticed a bug in the Digg-this plugin for my blog. The javascript with this plugin was causing errors with Internet Explorer. The problem must have been around for a week or so, before I noticed it. Now my site doesn’t get too many visitors, but I would have expected someone to complain about it.

Looking in my stats it becomes clear why no-one has. Though only a small and insignificant sample, the traffic at my blog is strongly IE averse. Just have a look at the stats:

Overview of important browsers

Pie Chart of Overview

PHP & Symfony & Web Development tschellenbach 11 Oct 2007 5 Comments

How the Rubicon Project is innovating Google’s online advertising business

Update: In the comments the founder of the Rubicon Project indicated that they will not be working directly with advertisers. My misperception was based on the TechCrunch write-up. The article below is based on the assumption that they would approach both sides of the problem. Since this is not the case, take this into account when reading the post.

Rubicon

The Rubicon Project is by far the most promising, exciting and revolutionizing startup of the moment. Their business has the potential to completely change the entire web-advertising industry.
They are trying to become an intermediary (of intermediaries) by offering:

  • Publishers: automated ad revenue optimization between networks
  • Advertisers: a central point to setup online advertising (wrong)

They display it in a graphical way in their beta overview video.

Rubicon Industry Value Chain Position

The Changing Industry

An intermediary like Rubicon profoundly changes the competitive landscape for ad networks such as Google Adsense.

Currently startups in the ad network business face a so called chicken and egg problem. Even if you have a technically great product, you will still need advertisers to get publishers and vice-versa. The advertising and sales efforts required in this industry are substantial and present a major obstacle for new companies to enter the market.

Now imagine a new market where Rubicon is the intermediary. Any new advertising network could instantly get its product of the ground by joining Rubicon. No longer are millions in capital needed for sales and promotion. When a startup is able to outperform the market incumbents, in terms of matching advertisements with visitors, it has its road to success paved. Not only does this benefit those startups, it also gives Rubicon’s clients access to the best performing advertising solutions.

Google was the first to get the match between ads and viewers somewhat right. Eager to be next in line seems to be the currently hyped Facebook, with its personalized advertising. My impression is that the current solutions are tremendously under-performing. When an intermediary, be it Rubicon, establishes a position for themselves, innovation in online advertising will boom.

An intermediary like Rubicon changes the balance between required core competences for ad networks. The focus moves from sales&marketing to clever algorithms, extensive data and intelligent models to match the advertisements with the viewers. The market’s changes effectively open up this 27 billion industry to a far larger array of entrepreneurs.

Google’s Perspective

Rubicon effectively lowers the barriers to entry into this Google Adsense dominated market. In the new market situation it will be harder for Google to stay ahead of its competition. This fact makes you wonder, will Google move to the Rubicon position or remain where it is. Or in other words, will it defend its sales competence or its ability to match visitors with advertisements?

If Rubicon succeeds we will soon have some very interesting entrepreneurial possibilities ahead. I certainly wish them the best of luck. Given the experience and progress of the founding team, I think they have a good shot.

@Frank, Good luck starting your sixth company!

Recommended Readings:

Techcrunch coverage of Rubicon

Rubicon about page

Blog of Frank Addante (I subscribed to it, great readings)

http://www.founderblog.com/2007/10/what-is-rubicon-project-part-ii-solving.html

http://www.founderblog.com/2007/10/what-is-rubicon-project.html

Rubicon beta overview video

Business & Web Development tschellenbach 08 Oct 2007 9 Comments

Ajax for unique usernames

I always enjoy the logical thinking required for programming. For Symfony it is really nice how it all just flows together. I wanted to implement an ajax check for unique usernames. Something similar like how you see it on Twitter. Lets get started:

First create a validator in lib/validators/sfUniqueUserValidator.class.php

<?php
class sfUniqueUserValidator extends sfValidator
{
public function execute (&$value, &$error)
{
//check if the username exists
$c = new Criteria();
$c->add(sfGuardUserPeer::USERNAME, $value);
$user = sfGuardUserPeer::doSelect($c);
if (!empty($user))
{
$error = $this->getParameter('user_error');

return false;
}

return true;
}

public function initialize ($context, $parameters = null)
{
// Initialize parent
parent::initialize($context);

// Set default parameters value
$this->setParameter('user_error', 'This username is taken');

// Set parameters
$this->getParameterHolder()->add($parameters);

return true;
}
}

Then in your view template use:

<?php echo observe_field('rusername', array(
      'update'   => 'userstatus',
      'url'      => 'sfGuardAuth/checkuser',
      'with' => "'id='+$('rusername').value",
  )) ?>

this will monitor an input field called rusername, and submit its value to the sfGuardAuth/checkuser internal url.

And to glue it all together, in the actions:

  public function executeCheckuser()
  {
     $username = $this->getRequestParameter('id');
     $userValidator = new sfUniqueUserValidator();
     $userValidator->initialize($this->getContext());
     $error='none';
     if (!$userValidator->execute($username,$error))
     return $this->renderText($username.' is taken');

     return $this->renderText($username.' is available'); 

  }

Enjoy!

Ps. any tips for posting code in wordpress would be greatly appreciated, for me it does the strangest types of things.

PHP & Symfony & Web Development tschellenbach 05 Oct 2007 10 Comments