Archive for April, 2009

Published by Rolf on 28 Apr 2009

Apache 2 compression the easy way

Gzip (or on Apache 2, Deflate) compression is a godsend.  Rather than try to deal with changing content headers in code, with a few simple httpd.conf settings you can get nearly all the advantages instantly.  Now possibly ancient browsers can choke on this, but it will work for everybody who has a browser built since the millenium – to accommodate the old folks we have a BrowserMatch condition. Add some simple expiration rules and you’ve probably sped up your serving by at least 70%.

Make sure you’ve enabled mod_deflate via LoadModule, then add this to the main section, or your virtual host(s).

httpd.conf

...
<IfModule mod_deflate.c>
 
# Netscape 4.X has some problems
BrowserMatch ^Mozilla/4 gzip-only-text/html
# Netscape 4.06-4.08 have some more problems
BrowserMatch ^Mozilla/4\.0[678] no-gzip
# MSIE masquerades as Netscape, but it is fine
BrowserMatch \bMSIE !no-gzip !gzip-only-text/html
# Make sure proxies don't deliver the wrong content
Header append Vary User-Agent env=!dont-vary
 
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE application/x-javascript
AddOutputFilterByType DEFLATE text/css
 
# turn on the module for this directory
ExpiresActive on
 
# cache common graphics for 3 days
ExpiresByType image/jpg 		"access plus 3 days"
ExpiresByType image/gif 		"access plus 3 days"
ExpiresByType image/jpeg 		"access plus 3 days"
ExpiresByType image/png 		"access plus 3 days"
 
# cache CSS and JS for 24 hours
ExpiresByType text/css 			"access plus 24 hours"
ExpiresByType application/x-javascript 	"access plus 24 hours"
</IfModule>

Some good links with more info here and here.

Published by Rolf on 25 Apr 2009

Time to upgrade to Internet Explorer 8

Ten minutes in, and I’m convinced it’s worth it; they made some huge productivity improvements for web developers in IE8. The built-in Developer Tools bring it up to par with Firefox’s Firebug. And I am thrilled that “View Source” finally has line numbers and syntax highlighting!

Get it now.

Published by Rolf on 22 Apr 2009

An ExtJS form helper for CakePHP

ExtJS allows you to make really elaborate Javascript-based UI once you get the hang of it.  Unfortunately (unless there is some project I missed) it hasn’t yet been integrated with CakePHP so you end up hand-coding a bunch of stuff in order to get simple stuff (like client-side form validation) to work.  The world is ready for a helper class, or at least I was.

So I put this together.  Currently it only supports text, password, and hidden fields but you get the idea.  The CakePHP FormHelper class inspired some of the logic dealing with camelcasing the field names. 

app/views/helpers/extjs.php

class ExtjsHelper extends AppHelper {
 
	function init()
	{
		return '<script language="javascript">				
				Ext.onReady(function(){
				Ext.QuickTips.init();	
				});
			</script>';
	}
	function input($fieldName, $options = array())
	{
		// see the API definition of FormHelper::input()
		$view =& ClassRegistry::getObject('view');
		$this->setEntity($fieldName);
 
		$tokens =  $view->entity();
		$modelname = $tokens[0];
		$propertyname = $tokens[1];
 
		if (isset($options['type']))
			$type = $options['type'];
		else
		{
			if ($fieldName == 'id')
				$type = "hidden";
			elseif (in_array($this->field(), array('psword', 'passwd', 'password')))
				$type = "password";
			else $type = "text";
		}
 
		$label = (isset($options['label']) ? $options['label'] : Inflector::humanize($propertyname));
		$id = $modelname.Inflector::camelize($propertyname);
		$html = '';
 
		$value = (isset($options['value']) ? $options['value'] : '');
		$style = (isset($options['style']) ? ',style:"'.$options['style'].'"' : '');
		$width = (isset($options['width']) ? ',width:"'.$options['width'].'"' : '');
		$vtype = (isset($options['vtype']) ? ',vtype:"'.$options['vtype'].'"' : '');
		$allowBlank = (isset($options['allowBlank']) ? ',allowBlank:'.($options['allowBlank'] ? 'true':'false') : '');
		$emptyText = (isset($options['emptyText']) ? ',emptyText:"'.$options['emptyText'].'"' : '');
 
		if ($type != "hidden")
			$html .= '<div class="x-form-item" tabindex="-1"><label class="x-form-item-label" for="ext-comp-1006">'.$label.'</label>
				<div class="x-form-element">';
		$html .= '<input type="'.$type.'" name="data['.$modelname.']['.strtolower($propertyname).']" id="'.$id.'" value="'.$value.'"/>';
		if ($type != "hidden")
			$html .= '</div><div class="x-form-clear-left"/></div>
				<script language="javascript">Ext.onReady(function(){
				new Ext.form.TextField({
				msgTarget:"side"
				'.$style.'
				'.$width.'
				'.$vtype.'
				'.$allowBlank.'
				'.$emptyText.'
				}).applyToMarkup(document.getElementById("'.$id.'"));
			});</script>';	
		return $html;		
	}	
}

 
Usage:

// include the ExtJS Javascript and CSS classes in the HEAD of your layout, 
// then create the form as usual
 
echo $form->create(...);
echo $extjs->init();
echo $extjs->input('email', 
		array(
			'style'=>'width:200px;margin:8px',
			'vtype'=>'email',
			'allowBlank' => false
			));
...
echo $form->end('Submit');

This will then post the same form data structure that Cake normally does.

Published by Rolf on 21 Apr 2009

Caching expensive actions with Jake

In an earlier post I described using the Jake bridge to get CakePHP working with Joomla. Overall the bridge works like a champ, with one exception I’ve found: it seems that Jake does not support cached actions – if you try to cache an action, it no longer renders through the Jake component. This is probably a bug in Jake.

Fortunately Jake does support cached elements though, so you can do this workaround to get your expensive operations cached.

  1. If you haven’t already, create the writable directory app/tmp/cache/views.
  2. Suppose your desired action is controllername/expensiveaction(). Move the logic into the new (private) function controllername/_expensiveactionimp() and make it return a $somedata variable that contains all the data you need to output. E.g.,
    function expensiveaction()
    {
      if (isset($this->params['requested']))
        return $this->_expensiveactionimp();
      else
      {
        // do other stuff
      }
    }
    function _expensiveactionimp()
    {
      // slow, expensive operations
      $somedata = do_slow_stuff();
      return $somedata;
    }
  3. Move the entire contents of the corresponding view to a new element: app/views/elements/expensiveaction.ctp. In this element, just do
    $somedata = $this->requestAction('controllername/expensiveaction);
  4. Now, in the view expensiveaction.ctp, just do (with the appropriate cache interval)
  5. echo $this->element('expensiveaction', array('cache' => '1 hour'));

Enjoy.

Published by Rolf on 10 Apr 2009

Configuring GNU Mailman on Plesk 8, part 2

I’ve learned a few more things in the process of doing this.  Since I needed to perform redirects to the new mailman lists (to accomodate legacy list names), it turns out that you have to use the Plesk control panel for this.  So after getting my lists working without Plesk, I had to recreate them in Plesk.  Not as bad as it sounds though.  Here are the steps:

  1. First, export the existing list members (I had over a hundred on one list and didn’t want to enter them by hand), and then delete the existing list.  The commands for this are:
    # cd /var/lib/mailman/lists
    # /usr/lib/mailman/bin/list_members LISTNAME > members.txt
    # /usr/lib/mailman/bin/rmlist LISTNAME
  2. Create the mailing list in Plesk, from Domains->DOMAINNAME->Mail->Mailing Lists, using the same LISTNAME as before
  3. Go to http://lists.DOMAINNAME/mailman/admin/LISTNAME to update the “terse phrase”, description, and other default settings.
  4. Go to Membership->Mass Subscription and paste in the names from the file you created in step 1.  You probably don’t want to send a welcome message to these people since they were already members before.

Now you can can set up redirects for your old list names in Plesk, and they work (sort of).  Received messages will no longer bounce back, but they will be held for the moderater with the reason (“Message has implicit destination”).  Not sure how to avoid this, but hopefully it’s not too much trouble to manage.  Good luck.

Next »