Emptying your inbox is a good start, but only half the problem for GTD

28 01 2009
done? Are we?Currently, I'm working on my 24hbc project rmindr.com and I know that even if I've launched an early alpha version to the public, it is far from ready, since I can't use it the way I want to yet.

My whole purpose for creating a GTD tool was that I felt a need for this type of service my self. Other services like Remember The Milk and just didn't do it for me. My main inspiration has been the excellent program Things which is perfect and do it just right, with a few things (no pun indended) that I'm missing.

The most important step for Getting Things Done is that there is absolutely no friction when collecting an idea. There is a reason why most computer geeks still have a pen and paper right next to their computer as it is a really fast way to get stuff out of your head. My approach to solving this problem is to offer as many fast and simple methods as possible for collecting ideas. I started implementing the one I thought was most useful - collecting by SMS, since I've at times been sending an SMS to my self to remind me of something.

The problem in everyday life with many different collection methods is that all those collected ideas end up in a bunch of different places or at best in one of many inboxes you might have. Having one inbox with everything is in my case the key to success. Well that is if I empty it of course, and even more importantly, actually do the stuff that was in the inbox. Because emptying the inbox is a good start but only half way there.

A key factor for me starting to use the system is that I create a system that is complete and that my brain really trusts is a better storage area for ideas rather than the brain itself. We'll see what happens. Firstly I must get rmindr to the next level where I can start trusting at least parts of the system, and then keep on working on it until I'm all the way there.

For me it was an absolute relief just to have somewhere to unload my brain of ideas when I first starting using GTD. In my case I used Things and mostly my Someday/Maybe list which by now is very long. I've implemented Inbox Zero with my email which works great for me even if I don't get super amounts of email (around 50 per day) but my inbox is empty now and is every night.

How would you like to collect ideas? I want to create a small app for OS X and Vista and others that run in the background and listens to a shortcut and opens a collect box from what ever application you are in. When clicking save, the message will be stored in your rmindr inbox. So if you are good at Cocoa or .Net and want to use my soon coming API, feel free to contact me here.

Reblog this post [with Zemanta]


Minipreneurship - What can be done in 24 hours?

24 01 2009

The answer is quite simply: A lot!

Yesterday at 12 noon the first 24 hour business camp ended. I participated with my Getting Things Done service rmindr.com that I first wrote about a while back.

My 24 hours consisted of everything between 4-hour-dig-downs in weird php errors (later saved by a Hjalmar php hack), and taking a soothing bath outside in 40 degrees water overlooking the snowcovered rocks in the Stockholm archipelago spa Hasseludden Yasuragi.

The event itself was without a doubt one of the best and most productive entrepreneurial events I've been to. The location certainly added to the excitement, but mostly it was the people's attitude towards entrepreneurship that made an impact on me. When the event ended, the participants were asked if they would want venture capital for their idea, and only about 3 out of 90 put their hands up.

I don't want to put too much into that because it is normally not 24 hour ideas that get funded anyways, but it shows that starting a service or product on the internet has never been easier or cheaper. These services will not likely be the next Google's or Amazon's, but it can easily be services that bring in some revenue for their creators. This type of "minipreneurship" is really starting to boom, and events like 24hbc is proof of that. You can easily start something in 24 hours if you put your mind to it and make sure to focus your idea.

Learning from my Seedcamp experience "focus" and "metrics" are the two most important words I've brought with me. So based on that this is how I focused by 24 hours at 24hbc:

  • Step 1: Make my development setup work on the production server. (to enable fast deploy during the development)
  • Step 2: Define basic functionality for the site to be valuable. For rmindr.com those features were:
    • Create actions with tags and notes
    • Access context lists through a private rss feed
    • Move actions to different focuses (inbox, next, someday/maybe, scheduled)
    • Collect ideas by sms from a cellphone
  • Step 3: Define advanced features that can be included if there is time left
  • Step 4: implement

I managed to get all of step 1 and 2 done to a level where they could be released in an alpha version. I never managed to get into the very necessary but quite advanced feature of organizing actions into projects in an infinite number of levels. That will come in the beta release. Maybe in another 24 hours... or not.

I've had this idea for almost a year now and I did the interface design this summer. So an event like 24hbc was exactly the push I needed to get it out there.

Thanks for any input regarding rmindr.com and feel free to vote for my project at http://www.24hourbusinesscamp.com.

Reblog this post [with Zemanta]


Great interview with David Allen on Getting Things Done

27 07 2008
IMG_4568Image by .nele via Flickr

As of lately I have been quite into the "Getting Things Done" methology. I'm very bad at implementing it myself, but I'm quite good at telling other people how and why they should use it. I just found this interview by Robert Scoble on fastcompany.tv where he interviews David Allen to get some of the core stuff of GTD out from him. http://www.fastcompany.tv/video/david-allen-on-getting-things-done

As I have been listening to one of David Allens seminars I've been completely sold on the idea of GTD. Although I've only implemented a small portion of the system I really feel a lot less stressed. Earlier I had problems sleeping because there were so many ideas floating around in my mind that I had to keep track of. Now I'm using a tool called Things to keep all those ideas for me. A huge relief not having to remember everything at all times.

The next step for me is to get really good at the weekly review. Because without having a good routine for that, everyone will fail to fully implement the system. I'm not saying that because David Allen says so, but because I'm 7 months into implementing the system, and I've noticed that I look at my lists far to seldom nowadays. If I do the weekly review, I will put the right stuff on the right list at all times and will probably check the lists more often.

That leads me to the biggest problem I have with the system. How do you keep your lists? It's quite good when I'm by my computer, but then at the store and so on there is no way that I would carry around a bunch of paper lists just to see what's on there. Now with the Iphone, there are a lot of new tools for keeping those lists, but in some ways I think it's a bit too complicated since it is still a platform specific solution.

Enter rmindr.com, my main on-the-side project. It's an online GTD app much like any other GTD app out there. With one big difference. The userinterface just works. It is highly inspired by Things (mentioned above) because that fells natural for me. I don't really think this service will make me rich or anything, but what it does is that it solves a problem, in particular My problem. With rmindr you get all of your context lists avaliable on any device through RSS. Collaboration is really simple with drag-n-drop ease to delegate tasks. Collection is really simple through the API where collecting a new task through any medium or tool will be possible, and as easy as a HTTP Post to a specific URL, or a MMS/SMS/Phonecall to a specific number.

With the RSS context lists I'll be able to see my grossery store shopping list with any phone while I'm in the store.

The service will be launching shortly as I'm putting the last bits and pieces together. Stay tuned for more info. I won't have a beta sign up page unfortunately, but if you subscribe to the rss of this blog you'll no when it opens.

Zemanta Pixie


Implementing Zend Layout and Smarty using Zend Framework MVC

12 03 2008

Lately I have received some requests on how Zend Layout can be used while not using the default templating system in Zend Framework (ZF), but instead using Smarty as a templating engine. So here is a write up on how to do so. The way to implement this may vary, so this is just one way of doing it. :)

This "tutorial" requires that you have at least some sense on how ZF works and a little bit on MVC in general.

1) Get Zend Studio for Eclipse as this will help A LOT on structuring your applications in a good way.

2) Document folder structure:

Having a good document folder structure will also help you build clean and maintainable applications. I try to mimic the ZF coding standard and naming conventions as much as possible. Here's my setup:

 
shared/
	lib/
 		Zend/
 		Smarty/
	mySiteName/
	 application/
	 	default/
	 		controllers/
	 		models/
	 		views/
	 			filters/
	 			helpers/
	 			layouts/
	 				default.tpl
	 				(layoutname).tpl
	 			scripts/
	 				(controller)/
	 					(action).tpl
	 config/
	 	config.xml
	 data/
	 	cache/
	 lib/
	 	EZ/
	 		View/
	 			Smarty/
	 				Plugin/
	 					Abstract.php
	 					Broker.php
	 					Exception.php
	 					Standard.php
	 			Smarty.php
	 public_html/
	 	css/
	 	js/
	 	pics/
	 	.htaccess
	 	index.php
	 templates_c/

3) The config file

 
< ?xml version="1.0"?>
<configdata>
<production>
        <webhost>www.somesite.com</webhost>
        <database>
            <adapter>mysqli</adapter>
<params>
            	   <host></host>
            	   <username></username>
<password></password>
             	   <dbname></dbname>
		</params>
        </database>
<libpath></libpath>
	<session_name>my_session</session_name>
	<base_url>http://www.somesite.com</base_url>
	<base_prefix>http://</base_prefix>
	<smarty>
		<compiledir></compiledir>
		<suffix>tpl</suffix>
	</smarty>
	<layout>
		<layoutpath></layoutpath>
		<contentkey>content</contentkey>
		<template>Special</template>
		<suffix>tpl</suffix>
	</layout>
	<debugging>false</debugging>
	<cache>
		<defaultlifetime>3600</defaultlifetime>
		<defaultcachedir>../data/cache/</defaultcachedir>
	</cache>
  </production>
 
  <staging extends="production">
	<debugging>true</debugging>
  </staging>
</configdata>
 

4) The bootstrap index.php

In order for things to work with Smarty and Zend Layout, we need to set som extra variables and inflector scripts so that view scripts will be resolved and parsed appropriately.

 
/**
 * ezApplicationBase - A Zend Framework implementation with Smarty and Zend Layout
 *
 * Bootstrap file for mysite.com
 *
 * @author Anders Fredriksson (http://anders.tyckr.com)
 * @version 0.1
 */
//three folders "above" index.php
define('ROOT_DIR', dirname(dirname(dirname(__FILE__))));
define('APP_DIR',dirname(dirname(__FILE__)));
 
set_include_path('.' . PATH_SEPARATOR . APP_DIR . '/lib/' . PATH_SEPARATOR . APP_DIR . '/application/default/models/' . PATH_SEPARATOR . ROOT_DIR . '/shared/lib/' . PATH_SEPARATOR . get_include_path());
//This requires that your Zend library lies in ROOT_DIR/shared/lib/
 
//make classes autoload without doing require
require_once('Zend/Loader.php');
Zend_Loader::registerAutoload();
 
if(defined('ENV') !== TRUE) {
	define('ENV','production');	//change staging to production to go to production settings
}
 
$config = new Zend_Config_Xml(APP_DIR . '/config/config.xml', ENV);
Zend_Registry::set('config',$config);
 
//init session
$session = new Zend_Session_Namespace($config->session_name);
Zend_Registry::set('session',$session);
 
Zend_Db_Table::setDefaultAdapter(Zend_Db::factory(Zend_Registry::get('config')->database));
 
/**
 * Init the Smarty view wrapper and set smarty suffix to the view scripts.
 */
$view = new EZ_View_Smarty($config->smarty->toArray());
 
//use the viewrenderer to keep the code DRY
//instantiate and add the helper in one go
$viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('ViewRenderer');
$viewRenderer->setView($view);
$viewRenderer->setViewSuffix($config->smarty->suffix);
 
/**
 * Set inflector for Zend_Layout
 */
$inflector = new Zend_Filter_Inflector(':script.:suffix');
$inflector->addRules(array(':script' => array('Word_CamelCaseToDash', 'StringToLower'),
			 								  'suffix'  => $config->layout->suffix));
 
// Initialise Zend_Layout's MVC helpers
Zend_Layout::startMvc(array('layoutPath' => ROOT_DIR.$config->layout->layoutPath,
							'view' => $view,
							'contentKey' => $config->layout->contentKey,
							'inflector' => $inflector));
 
$front = Zend_Controller_Front::getInstance();
 
$front->setControllerDirectory(array(
    'default' => '../application/default/controllers',
    'blog'    => '../application/blog/controllers',
));
 
$front->throwExceptions(true);
 
//enable logging to default.log
$writer = new Zend_Log_Writer_Stream(APP_DIR.'/data/log/default.log');
$logger = new Zend_Log($writer);
 
//give easy access to the logger
Zend_Registry::set('logger',$logger);
 
try {
    $front->dispatch();
} catch(Exception $e) {
    echo nl2br($e->__toString());
}

5) The Smarty implementation of Zend_View. For this I want to give Naneau credit as it is mostly the same as the implementation he did a while back.

EZ/View/Smarty.php

 
/**
 *
 * @since Dec 7 2007
 * @author Anders Fredriksson
 */
/**
 * Zend View Base Class
 */
require_once 'Zend/View.php';
 
/**
 * Smarty templating engine
 */
require_once 'Smarty/Smarty.class.php';
 
/**
 * @category
 */
class EZ_View_Smarty extends Zend_View_Abstract {
	/**
	 * Smarty object
	 * @var Smarty
	 */
	protected $_smarty;
 
	protected $_plugins;
 
	/**
	 * Constructor
	 *
	 * Pass it a an array with the following configuration options:
	 *
	 * scriptPath: the directory where your templates reside
	 * compileDir: the directory where you want your compiled templates (must be
	 * writable by the webserver)
	 * configDir: the directory where your configuration files reside
	 *
	 * both scriptPath and compileDir are mandatory options, as Smarty needs
	 * them. You can't set a cacheDir, if you want caching use Zend_Cache
	 * instead, adding caching to the view explicitly would alter behaviour
	 * from Zend_View.
	 *
	 * @see Zend_View::__construct
	 * @param array $config ["scriptPath" => /path/to/templates,
	 *			     "compileDir" => /path/to/compileDir,
	 *			     "configDir"  => /path/to/configDir ]
	 * @throws Exception
	 */
	public function __construct($config = array()) {
		$this->_smarty = new Smarty ( );
		//smarty object
 
		if (! array_key_exists ( 'compileDir', $config )) {
			throw new Exception ( 'compileDir must be set in $config for ' . get_class ( $this ) );
		} else {
			$this->_smarty->compile_dir = $config ['compileDir'];
		}
		//compile dir must be set
		$this->_smarty->debugging = true;
		if (array_key_exists ( 'configDir', $config )) {
			$this->_smarty->config_dir = $config ['configDir'];
		}
		//configuration files directory
 
		parent::__construct ( $config );
		//call parent constructor
 
		$this->_plugins = new EZ_View_Smarty_Plugin_Broker($this);
		$this->registerPlugin(new EZ_View_Smarty_Plugin_Standard());
 
	}
 
	public function setCompilePath($dir) {
		$this->_smarty->compile_dir = $dir;
		return $this;
	}
 
	/**
	 * Return the template engine object
	 *
	 * @return Smarty
	 */
	public function getEngine() {
		return $this->_smarty;
	}
 
	/**
	 * register a new plugin
	 *
	 * @param EZ_View_Smarty_Plugin_Abstract
	 */
	public function registerPlugin(EZ_View_Smarty_Plugin_Abstract $plugin,$stackIndex = null) {
		$this->_plugins->registerPlugin ( $plugin, $stackIndex );
		return $this;
	}
 
	/**
	 * Unregister a plugin
	 *
	 * @param string|EZ_View_Smarty_Plugin_Abstract $plugin Plugin object or class name
	 */
	public function unRegisterPlugin($plugin) {
		$this->_plugins->registerPlugin ( $plugin );
		return $this;
	}
 
	/**
	 * fetch a template, echos the result,
	 *
	 * @see Zend_View_Abstract::render()
	 * @param string $name the template
	 * @return void
	 */
	protected function _run() {
		$this->strictVars ( true );
 
		$vars = get_object_vars ( $this );
		foreach ( $vars as $key => $value ) {
			if ('_' != substr ( $key, 0, 1 )) {
				$this->_smarty->assign ( $key, $value );
			}
		}
		//assign variables to the template engine
 
		$this->_smarty->assign_by_ref ( 'this', $this );
		//why 'this'?
		//to emulate standard zend view functionality
		//doesn't mess up smarty in any way
 
		$path = $this->getScriptPaths ();
 
		$file = substr ( func_get_arg ( 0 ), strlen ( $path [0] ) );
		//smarty needs a template_dir, and can only use templates,
		//found in that directory, so we have to strip it from the filename
 
		$this->_smarty->template_dir = $path [0];
		//set the template diretory as the first directory from the path
 
		echo $this->_smarty->fetch ( $file );
		//process the template (and filter the output)
	}
}
 

My additions are basically the possibility to add plugins to Smarty without putting them in the Smarty lib directory. This is needed so that a nicer implementation of ZF's view helpers can be used.

6) Smarty plugin loader

EZ/View/Smarty/Plugin/Standard.php

 
 
class EZ_View_Smarty_Plugin_Standard extends EZ_View_Smarty_Plugin_Abstract {
 
	public static function layoutFunction($params, &$smarty) {
		$section = $params ['section'];
		$toReturn = "";
		try {
			//oppress errors
			@$toReturn = $smarty->_tpl_vars ['this']->placeholder ( 'Zend_Layout' )->$section;
			return $toReturn;
		} catch ( Exception $e ) {
			return $toReturn;
		}
	}
 
	public static function headTitleFunction($params, &$smarty) {
		$method = (isset ( $params ['method'] )) ? $params ['method'] : - 1;
		unset ( $params ['method'] );
		$args = (isset ( $params ['args'] )) ? self::parseArgs ( $params ['args'] ) : null;
		$toReturn = "";
		try {
			//oppress errors
			if ($method != - 1) {
				@$toReturn = $smarty->_tpl_vars ['this']->headTitle ()->$method ( $args );
			} else {
				$toReturn = $smarty->_tpl_vars ['this']->headTitle ();
			}
			return $toReturn;
		} catch ( Exception $e ) {
			return $toReturn;
		}
	}
 
	public static function headScriptFunction($params, &$smarty) {
		$method = (isset ( $params ['method'] )) ? $params ['method'] : - 1;
		unset ( $params ['method'] );
		$args = (isset ( $params ['args'] )) ? self::parseArgs ( $params ['args'] ) : null;
		$toReturn = "";
		try {
			if ($method != - 1) {
				//oppress errors
				if (is_array ( $args )) {
					//$toReturn = call_user_func('$smarty->_tpl_vars["this"]->headScript()->$method()',$args,true);
					//print "args src= ".$args['src'];
					//TODO: fix args usage
					$toReturn = $smarty->_tpl_vars ['this']->headScript ()->$method ( $args ['src'] );
				} else {
					@$toReturn = $smarty->_tpl_vars ['this']->headScript ()->$method ( $args );
				}
			} else {
				$toReturn = $smarty->_tpl_vars ['this']->headScript ();
			}
			return $toReturn;
		} catch ( Exception $e ) {
			return $toReturn;
		}
	}
 
	public static function jsLocaleFunction($params, &$smarty) {
		$method = (isset ( $params ['method'] )) ? $params ['method'] : - 1;
		unset ( $params ['method'] );
		$args = (isset ( $params ['args'] )) ? self::parseArgs ( $params ['args'] ) : null;
		$toReturn = "";
		try {
			if ($method != - 1) {
				//oppress errors
				if (is_array ( $args )) {
					//$toReturn = call_user_func('$smarty->_tpl_vars["this"]->headScript()->$method()',$args,true);
					//print "args src= ".$args['src'];
					//TODO: fix args usage
					$toReturn = $smarty->_tpl_vars ['this']->jsLocale ()->$method ( $args ['src'] );
				} else {
					@$toReturn = $smarty->_tpl_vars ['this']->jsLocale ()->$method ( $args );
				}
			} else {
				$toReturn = $smarty->_tpl_vars ['this']->jsLocale ();
			}
			return $toReturn;
		} catch ( Exception $e ) {
			return $toReturn;
		}
	}
 
	public static function headStyleFunction($params, &$smarty) {
		$method = (isset ( $params ['method'] )) ? $params ['method'] : - 1;
		unset ( $params ['method'] );
		$args = (isset ( $params ['args'] )) ? self::parseArgs ( $params ['args'] ) : null;
		$toReturn = "";
		try {
			if ($method != - 1) {
				//oppress errors
				if (is_array ( $args )) {
					//$toReturn = call_user_func('$smarty->_tpl_vars["this"]->headStyle()->$method()',$args,true);
					//print "args src= ".$args['src'];
					//TODO: fix args usage
					$toReturn = $smarty->_tpl_vars ['this']->headStyle ()->$method ( $args ['src'] );
				} else {
					@$toReturn = $smarty->_tpl_vars ['this']->headStyle ()->$method ( $args );
				}
			} else {
				$toReturn = $smarty->_tpl_vars ['this']->headStyle ();
			}
			return $toReturn;
		} catch ( Exception $e ) {
			return $toReturn;
		}
	}
 
	public static function parseArgs($args) {
		if (preg_match_all ( "/([^\\[^\\]]+)/", $args, $matches )) {
			$params = explode ( ',', $matches [1] [0] );
			$toReturn = array ( );
			foreach ( $params as $p ) {
				@list ( $key, $value ) = explode ( '=>', $p );
				$toReturn [preg_replace ( "/[\\' ]+/", "", $key )] = preg_replace ( "/[\\' ]+/", "", $value );
			}
			return $toReturn;
		}
		return $args;
	}
 
	/**
	 * Smarty block function, provides gettext support for smarty.
	 *
	 * The block content is the text that should be translated.
	 *
	 * Any parameter that is sent to the function will be represented as %n in the translation text,
	 * where n is 1 for the first parameter. The following parameters are reserved:
	 *   - escape - sets escape mode:
	 *       - 'html' for HTML escaping, this is the default.
	 *       - 'js' for javascript escaping.
	 *       - 'url' for url escaping.
	 *       - 'no'/'off'/0 - turns off escaping
	 *   - plural - The plural version of the text (2nd parameter of ngettext())
	 *   - count - The item count for plural mode (3rd parameter of ngettext())
	 */
	public static function tBlock($params, $text, &$smarty) {
		$text = stripslashes ( $text );
 
		// set escape mode
		if (isset ( $params ['escape'] )) {
			$escape = $params ['escape'];
			unset ( $params ['escape'] );
		}
 
		// set plural version
		if (isset ( $params ['plural'] )) {
			$plural = $params ['plural'];
			unset ( $params ['plural'] );
 
			// set count
			if (isset ( $params ['count'] )) {
				$count = $params ['count'];
				unset ( $params ['count'] );
			}
		}
 
		// use plural if required parameters are set
		if (isset ( $count ) && isset ( $plural )) {
			$text = EZ_Language::getInstance ()->translatePlural ( $text, $plural, $count );
		} else { // use normal
			$text = EZ_Language::getInstance ()->translate ( $text );
		}
 
		// run strarg if there are parameters
		if (count ( $params )) {
			$text = self::strarg ( $text, $params );
		}
 
		if (! isset ( $escape ) || $escape == 'html') { // html escape, default
			$text = nl2br ( htmlspecialchars ( $text ) );
		} elseif (isset ( $escape )) {
			switch ( $escape) {
				case 'javascript' :
				case 'js' :
					// javascript escape
					$text = str_replace ( '\'', '\\\'', stripslashes ( $text ) );
				break;
				case 'url' :
					// url escape
					$text = urlencode ( $text );
				break;
			}
		}
 
		return $text;
	}
 
/**
 * Replaces arguments in a string with their values.
 * Arguments are represented by % followed by their number.
 *
 * @param	string	Source string
 * @param	mixed	Arguments, can be passed in an array or through single variables.
 * @returns	string	Modified string
 */
	public static function strarg($str) {
	$tr = array();
	$p = 0;
 
	for ($i=1; $i < func_num_args(); $i++) {
		$arg = func_get_arg($i);
 
		if (is_array($arg)) {
			foreach ($arg as $aarg) {
				$tr['%'.++$p] = $aarg;
			}
		} else {
			$tr['%'.++$p] = $arg;
		}
	}
 
	return strtr($str, $tr);
  }
}
 

This file is basically to allow for view helper function calls that are needed to get Zend Layout to work properly. The Smarty compiler has a limitation of just allowing one level of function calls {$this->someHelper()} works, but {$this->someHelper()->someHelperFunction()} does not work. Zend Layout requires you to do $this->placeholder("Zend_Layout")->section to output the data inside the layoutsection "section". With my helper the syntax for that would be {layout section='section'}

The other helpers in there are some of the standard helpers that comes with Zend View. I kept them in there to give you an idea of what can be done.

The main purpose for using Zend Layout with Smarty is that you can give your site a clean look without having to duplicate code.

7) The default template for the layout. I use YUI Grids CSS to get a good clean standard css layout.

 
<xml version="1.0" encoding="UTF-8" />
{$this->doctype()}
<html xmlns='http://www.w3.org/1999/xhtml' xml:lang="en" lang="en">
<head>
	{headTitle}
	{headScript}
	{$this->headLink()}
<link rel="stylesheet" href="http://yui.yahooapis.com/2.4.1/build/reset-fonts-grids/reset-fonts-grids.css" type="text/css">
	{headStyle}
	{$this->headMeta()}
	{*debugging turned on as a default during development *}
	{if $debugging}
		{debug}
	{/if}
</link></head>
<body>
<div id="custom-doc" class="yui-t2">
<div id="hd" class="header">{$this->placeholder("header")}</div>
<div id="bd">
<div id="yui-main">
<div class="yui-b">
<div class="yui-g">
	    			<!-- YOUR DATA GOES HERE -->
	    			{layout section="content"}
	    		</div>
</div>
</div>
<div id="leftColumn" class="yui-b"><!-- YOUR NAVIGATION GOES HERE -->
	    {$this->placeholder("leftColumn")}
	    </div>
</div>
<div id="ft">{$this->placeholder("footer")}</div>
</div>
 
</body>
</html>
 

8) Get some action to render into a section, eg. "leftColumn".

For this to work with a Zend Layout section you need to set the responseSegmentName of that action, which is done with:

$this->_helper->viewRenderer->setResponseSegment('section');

Doing this will probably put you in a rough spot when you try to make calls to specific actions from within other actions or preDispatch. To avoid this there is a simpler method that I just switched to.

To render action "testAction()" from OtherController into a "section" leftColumn for instance, and to do that from another action call "indexAction()" we do the following:

 
public function indexAction() {
    $this->view->placeholder('leftColumn')->set($this->view->action('test','Other'));
    $this->view->someVar = "this will get rendered into the index.tpl";
}
 

And that pretty much wraps it up for now.

Please feel free to ask me any questions.

Update: I guess it will help more if I post the code for the Smarty plugin helpers :)

EZ/View/Smarty/Plugin/Abstract.php

 
abstract class EZ_View_Smarty_Plugin_Abstract {
 
	protected $_functionRegistry;
	protected $_namingPattern ='/([a-zA-Z1-9]+)(Function|Block)$/';
 
	/**
	 *
	 */
	function __construct() {
 
	}
 
	/**
	 * @return array
	 */
	public function getClassFunctionArray() {
		$type = get_class($this);
		$methods = get_class_methods($this);
		foreach($methods as $value) {
			if(preg_match($this->_namingPattern,$value,$matches)) {
				$this->_functionRegistry[$matches[1]] = $type."::".$value;
			}
		}
		return $this->_functionRegistry;
	}
 
	/**
	 * change the default naming pattern for functions that should be mapped to smarty functions
	 */
	public function setNamingPattern($pattern ='/([a-zA-Z1-9]+)(Function|Block)$/') {
		 //"/([a-zA-Z1-9]+)Function$/";
		 $this->_namingPattern = $pattern;
	}
}
 

EZ/View/Smarty/Plugin/Broker.php:

 
/**
 * EZ_View_Smarty_Plugin_Broker
 *
 * This class registers smarty plugins with the current smarty view
 */
class EZ_View_Smarty_Plugin_Broker {
 
	/**
	 * Array of instance of objects extending EZ_View_Smarty_Plugin_Abstract
	 *
	 * @var array
	 */
	protected $_plugins = array ( );
	protected $_view;
 
	/**
	 *
	 * @param EZ_View_Smarty $view
	 */
	public function __construct($view) {
		$this->_view = $view;
	}
 
	/**
	 * Register a plugin.
	 *
	 * @param  EZ_View_Smarty_Plugin_Abstract $plugin
	 * @param  int $stackIndex
	 * @return EZ_View_Smarty_Plugin_Broker
	 */
	public function registerPlugin(EZ_View_Smarty_Plugin_Abstract $plugin,$stackIndex = null) {
		if (false !== array_search ( $plugin, $this->_plugins, true )) {
			throw new EZ_View_Smarty_Exception ( 'Plugin already registered' );
		}
 
		$stackIndex = ( int ) $stackIndex;
 
		if ($stackIndex) {
			if (isset ( $this->_plugins [$stackIndex] )) {
				throw new EZ_View_Smarty_Exception ( 'Plugin with stackIndex "' . $stackIndex . '" already registered' );
			}
			$this->_plugins [$stackIndex] = $plugin;
		} else {
			$stackIndex = count ( $this->_plugins );
			while ( isset ( $this->_plugins [$stackIndex] ) ) {
				++ $stackIndex;
			}
			$this->_plugins [$stackIndex] = $plugin;
		}
 
		ksort ( $this->_plugins );
		$this->reLoadPlugins();
		return $this;
	}
 
	/**
	 * Unregister a plugin.
	 *
	 * @param string|EZ_View_Smarty_Plugin_Abstract $plugin Plugin object or class name
	 * @return EZ_View_Smarty_Plugin_Broker
	 */
	public function unregisterPlugin($plugin) {
		if ($plugin instanceof EZ_View_Smarty_Plugin_Abstract) {
			// Given a plugin object, find it in the array
			$key = array_search ( $plugin, $this->_plugins, true );
			if (false === $key) {
				throw new EZ_View_Smarty_Exception ( 'Plugin never registered.' );
			}
			unset ( $this->_plugins [$key] );
		} elseif (is_string ( $plugin )) {
			// Given a plugin class, find all plugins of that class and unset them
			foreach ( $this->_plugins as $key => $_plugin ) {
				$type = get_class ( $_plugin );
				if ($plugin == $type) {
					unset ( $this->_plugins [$key] );
				}
			}
		}
		$this->reLoadPlugins();
		return $this;
	}
 
	/**
	 * Is a plugin of a particular class registered?
	 *
	 * @param  string $class
	 * @return bool
	 */
	public function hasPlugin($class) {
		$found = array ( );
		foreach ( $this->_plugins as $plugin ) {
			$type = get_class ( $plugin );
			if ($class == $type) {
				return true;
			}
		}
 
		return false;
	}
 
	/**
	 * Retrieve a plugin or plugins by class
	 *
	 * @param  string $class Class name of plugin(s) desired
	 * @return false|EZ_View_Smarty_Plugin_Abstract|array Returns false if none found, plugin if only one found, and array of plugins if multiple plugins of same class found
	 */
	public function getPlugin($class) {
		$found = array ( );
		foreach ( $this->_plugins as $plugin ) {
			$type = get_class ( $plugin );
			if ($class == $type) {
				$found [] = $plugin;
			}
		}
 
		switch ( count ( $found )) {
			case 0 :
				return false;
			case 1 :
				return $found [0];
			default :
				return $found;
		}
	}
 
	/**
	 * Retrieve all plugins
	 *
	 * @return array
	 */
	public function getPlugins() {
		return $this->_plugins;
	}
 
	/**
	 * Load all plugins
	 */
	public function loadPlugins() {
		foreach($this->_plugins as $plugin) {
			$type = get_class($plugin);
			//get an array of all the classfunctions that should be mapped to smarty functions
			$functions = $plugin->getClassFunctionArray($type);
			foreach($functions as $key => $value) {
				if(preg_match('/Block$/',$value)) {
					$this->_view->getEngine()->register_block($key,$value);
				} else {
					$this->_view->getEngine()->register_function($key,$value);
				}
			}
 
		}
	}
 
	public function unLoadPlugins() {
		foreach($this->_plugins as $plugin) {
			$type = get_class($plugin);
			//get an array of all the classfunctions that should be mapped to smarty functions
			$functions = $plugin->getClassFunctionArray($type);
			foreach($functions as $key => $value) {
				if(preg_match('/Block$/',$value)) {
					$this->_view->getEngine()->unregister_block($key);
				} else {
					$this->_view->getEngine()->unregister_function($key);
				}
			}
 
		}
	}
 
	public function reLoadPlugins() {
		$this->unLoadPlugins();
		$this->loadPlugins();
	}
}
 

Now, that should do it.



Error c00ce56e in ie

4 03 2008

Quite an interesting problem.

If you set encoding headers for XMLHttpRequests to header('Content-Type' , 'text/html; charset=utf8'); instead of header('Content-Type', 'text/html; charset=UTF-8'); IE7 will give the nice and clear error c00ce56e.

So if you ever get this problem, here's the solution.

update:

Sorry for my brief description of the problem. The problem occurs when setting the charset of a response to a XMLHttpRequest to utf8 instead of UTF-8. IE7 is very strict on naming of the charsets, while Firefox and IE6 (!!!) are a bit more lenient.

I read Microsofts description of the problem at http://support.microsoft.com/kb/304625 where they describe it as "Behavior is by design". :)



The right step towards Social Graph Freedom

2 02 2008

Google launched yesterday an API for their index of current relationships that exists on the internet today, ie social graphs. The great move here by Google is that instead of just giving access to other social networks, they actually index the open standards that already exist today such as XHML Friends Network (XFN) and Friend of a Friend (FOAF). These standards are based on simple XHML markup that as microformats are integrated in the site as meta information which I think really is how the future will look. It will probably take pretty long before this hits big but it is definitely the right step towards Social Graph freedom and also a step towards the Semantic Web. See the video below of Brad Fitzpatrick explaining it all.



Is the release ghost haunting Zend?

24 01 2008

[update] Apparently since I posted this they fixed the problems...

Since yesterday when Zend released their first real version of Zend Studio for Eclipse (which I've been using ever since I found the beta, a must have for any serious php developer) their site have been quite slow and problems loading pages and so on. Today I figured they would have fixed it but when trying to reach their product site it was blank. Then I tried to just remove "/studio/" in their URL and I got the following:

Zend messed up

Which I thought was rather funny considering that it is Zend we are talking about here. I mean, come on... its not that difficult to add a noRouteAction which could redirect to the correct place. If the makers of PHP have problems using their new platform (which also was released and also shows a white page) then how much problems am I going to run into? :)

I'm happy with the free beta of Zend Studio for Eclipse, and now I'm only waiting for the release of Zend Framework version 1.5 which include some really cool stuff.