Zend Framework Smarty Integration

I’ve been working with zend framework for some time and have been using the Smarty template engine for the views. Initially we were working well with the smarty integration but have just come across a problem. I’ve been using partials for Zend Navigation elements so I can create custom layouts.

The problem I was experiencing was that all my assigned variables were being cleared after the navigation partial has been called. The cause of this problem is that when partials are rendered the view object gets cloned to make sure that the partial is rendered in it’s own address space. With my previous integration the view object was not being cloned correctly and the current view object was being returned and then all the variables in the object was being cleared.

To fix this I found a new smarty integration on the internet based on the currently used view integration and adopted this into my object… I’ve included it below!

/**
* Smarty template engine integration into Zend Framework
* Some ideas borrowed from http://devzone.zend.com/article/120
*/
class ZendExt_View_Smarty extends Zend_View_Abstract
{
	/**
	 * Instance of Smarty
	 * @var Smarty
	 */
	protected $_smarty = null;
 
	/**
	 * Template explicitly set to render in this view
	 * @var string
	 */
	protected $_customTemplate = '';
 
	/**
	 * Smarty config
	 * @var array
	 */
	private $_config = null;
 
	/**
	 * Class definition and constructor
	 *
	 * Let's start with the class definition and the constructor part. My class Travello_View_Smarty is extending the Zend_View_Abstract class. In the constructor the parent constructor from Zend_View_Abstract is called first. After that a Smarty object is instantiated, configured and stored in a private attribute.
	 * Please note that I use a configuration object from the object store to get the configuration data for Smarty.
	 *
	 * @param array $smartyConfig
	 * @param array $config
	 */
	public function __construct($smartyConfig, $config = array())
	{
		$this->_config = $smartyConfig;
		parent::__construct($config);
		$this->_loadSmarty();
	}
 
	/**
	 * Return the template engine object
	 *
	 * @return Smarty
	 */
	public function getEngine()
	{
		return $this->_smarty;
	}
 
	/**
	 * Implement _run() method
	 *
	 * The method _run() is the only method that needs to be implemented in any subclass of Zend_View_Abstract. It is called automatically within the render() method. My implementation just uses the display() method from Smarty to generate and output the template.
	 *
	 * @param string $template
	 */
	protected function _run()
	{
		$file = func_num_args() > 0 && file_exists(func_get_arg(0)) ? func_get_arg(0) : '';
		if ($this->_customTemplate || $file) {
			$template = $this->_customTemplate;
			if (!$template) {
				$template = $file;
			}
 
			$this->_smarty->display($template);
		} else {
			throw new Zend_View_Exception('Cannot render view without any template being assigned or file does not exist');
		}
	}
 
	/**
	 * Overwrite assign() method
	 *
	 * The next part is an overwrite of the assign() method from Zend_View_Abstract, which works in a similar way. The big difference is that the values are assigned to the Smarty object and not to the $this->_vars variables array of Zend_View_Abstract.
	 *
	 * @param string|array $var
	 * @return Ext_View_Smarty
	 */
	public function assign($var, $value = null)
	{
		if (is_string($var)) {
			$this->_smarty->assign($var, $value);
		} elseif (is_array($var)) {
			foreach ($var as $key => $value) {
				$this->assign($key, $value);
			}
		} else {
			throw new Zend_View_Exception('assign() expects a string or array, got '.gettype($var));
		}
	}
 
	/**
	 * Overwrite escape() method
	 *
	 * The next part is an overwrite of the escape() method from Zend_View_Abstract. It works both for string and array values and also uses the escape() method from the Zend_View_Abstract. The advantage of this is that I don't have to care about each value of an array to get properly escaped.
	 *
	 * @param mixed $var
	 * @return mixed
	 */
	public function escape($var)
	{
		if (is_string($var)) {
			return parent::escape($var);
		} elseif (is_array($var)) {
			foreach ($var as $key => $val) {
				$var[$key] = $this->escape($val);
			}
		}
		return $var;
	}
 
	/**
	 * Print the output
	 *
	 * The next method output() is a wrapper on the render() method from Zend_View_Abstract. It just sets some headers before printing the output.
	 *
	 * @param <type> $name
	 */
	public function output($name)
	{
		header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
		header("Cache-Control: no-cache");
		header("Pragma: no-cache");
		header("Cache-Control: post-check=0, pre-check=0", false);
 
		print parent::render($name);
	}
 
	/**
	 * Use Smarty caching
	 *
	 * The last two methods were created to simply integrate the Smarty caching mechanism in the View class. With the first one you can check for cached template and with the second one you can set the caching on or of.
	 *
	 * @param string $template
	 * @return bool
	 */
	public function isCached($template)
	{
		return $this->_smarty->is_cached($template);
	}
 
	/**
	 * Enable/disable caching
	 *
	 * @param bool $caching
	 * @return Ext_View_Smarty
	 */
	public function setCaching($caching)
	{
		$this->_smarty->caching = $caching;
		return $this;
	}
 
	/**
	 * Template getter (return file path)
	 * @return string
	 */
	public function getTemplate()
	{
		return $this->_customTemplate;
	}
 
	/**
	 * Template filename setter
	 * @param string
	 * @return Ext_View_Smarty
	 */
	public function setTemplate($tpl)
	{
		$this->_customTemplate = $tpl;
		return $this;
	}
 
	/**
	 * Magic setter for Zend_View compatibility. Performs assign()
	 *
	 * @param string $key
	 * @param mixed $val
	 */
	public function __set($key, $val)
	{
		$this->assign($key, $val);
	}
 
 
	/**
	 * Magic getter for Zend_View compatibility. Retrieves template var
	 *
	 * @param string $key
	 * @return mixed
	 */
	public function __get($key)
	{
		return $this->_smarty->getTemplateVars($key);
	}
 
	/**
	 * Magic getter for Zend_View compatibility. Removes template var
	 *
	 * @see View/Zend_View_Abstract::__unset()
	 * @param string $key
	 */
	public function __unset($key)
	{
		$this->_smarty->clearAssign($key);
	}
 
	/**
	 * Allows testing with empty() and isset() to work
	 * Zend_View compatibility. Checks template var for existance
	 *
	 * @param string $key
	 * @return boolean
	 */
	public function __isset($key)
	{
		return (null !== $this->_smarty->getTemplateVars($key));
	}
 
	/**
	 * Zend_View compatibility. Retrieves all template vars
	 *
	 * @see Zend_View_Abstract::getVars()
	 * @return array
	 */
	public function getVars()
	{
		return $this->_smarty->getTemplateVars();
	}
 
	/**
	 * Updates Smarty's template_dir field with new value
	 *
	 * @param string $dir
	 * @return Ext_View_Smarty
	 */
	public function setTemplateDir($dir)
	{
		$this->_smarty->setTemplateDir($dir);
		return $this;
	}
 
	/**
	 * Adds another Smarty template_dir to scan for templates
	 *
	 * @param string $dir
	 * @return Ext_View_Smarty
	 */
	public function addTemplateDir($dir)
	{
		$this->_smarty->addTemplateDir($dir);
		return $this;
	}
 
	/**
	 * Adds another Smarty plugin directory to scan for plugins
	 *
	 * @param string $dir
	 * @return Ext_View_Smarty
	 */
	public function addPluginDir($dir)
	{
		$this->_smarty->addPluginsDir($dir);
		return $this;
	}
 
	/**
	 * Zend_View compatibility. Removes all template vars
	 *
	 * @see View/Zend_View_Abstract::clearVars()
	 * @return Ext_View_Smarty
	 */
	public function clearVars()
	{
		$this->_smarty->clearAllAssign();
		$this->assign('this', $this);
		return $this;
	}
 
	/**
	 * Zend_View compatibility. Add the templates dir
	 *
	 * @see View/Zend_View_Abstract::addBasePath()
	 * @return Ext_View_Smarty
	 */	public function addBasePath($path, $classPrefix = 'Zend_View')
	{
		parent::addBasePath($path, $classPrefix);
		$this->addScriptPath($path . '/templates');
		$this->addTemplateDir($path . '/templates/static');
		return $this;
	}
 
	/**
	 * Zend_View compatibility. Set the templates dir instead of scripts
	 *
	 * @see View/Zend_View_Abstract::setBasePath()
	 * @return Ext_View_Smarty
	 */
	public function setBasePath($path, $classPrefix = 'Zend_View')
	{
		parent::setBasePath($path, $classPrefix);
		$this->setScriptPath($path . '/templates');
		$this->addTemplateDir($path . '/templates/static');
		return $this;
	}
 
	/**
	 * Magic clone method, on clone create diferent smarty object
	 */
	public function __clone() {
		$this->_loadSmarty();
	}
 
	/**
	 * Initializes the smarty and populates config params
	 *
	 * @throws Zend_View_Exception
	 * @return void
	 */
	private function _loadSmarty()
	{
		if (!class_exists('Smarty', true)) {
			require_once 'Smarty/Smarty.class.php';
		}
 
		$this->_smarty = new Smarty();
 
		if ($this->_config === null) {
			throw new Zend_View_Exception("Could not locate Smarty config - node 'smarty' not found");
		}
 
		$this->_smarty->caching = $this->_config['smarty']['caching'];
		$this->_smarty->cache_lifetime = $this->_config['smarty']['cache_lifetime'];
		$this->_smarty->template_dir = $this->_config['smarty']['template_dir'];
		$this->_smarty->compile_dir = $this->_config['smarty']['compile_dir'];
		$this->_smarty->config_dir = $this->_config['smarty']['config_dir'];
		$this->_smarty->cache_dir = $this->_config['smarty']['cache_dir'];
		$this->_smarty->left_delimiter = $this->_config['smarty']['left_delimiter'];
		$this->_smarty->right_delimiter = $this->_config['smarty']['right_delimiter'];
 
		if(isset($this->_config['smarty']['plugins_dir']))
		{
			foreach($this->_config['smarty']['plugins_dir'] as $pluginDir)
			{
				$this->addPluginDir($pluginDir);
			}
		}
 
		$this->assign('this', $this);
	}
 
 
 
	public function addScriptPath($path)
	{
		if(!isset($this->_smarty->template_dir))
		{
			$this->_smarty->template_dir = $path;
		}
		else
		{
			if(!$this->checkScriptPathExists($path))
			{
				if(!is_array($this->_smarty->template_dir))
				{
					$this->_smarty->template_dir = array($this->_smarty->template_dir);
				}
 
				array_unshift($this->_smarty->template_dir, $path);
			}
		}
 
		parent::addScriptPath($path);
	}
 
	private function checkScriptPathExists($path)
	{
		$dirs = $this->_smarty->template_dir;
 
		if(is_array($dirs))
		{
			foreach($dirs as $dir)
			{
				if($dir == $path)
				{
					return true;
				}
			}
		}
		else
		{
			if($dirs == $path)
			{
				return true;
			}
		}
		return false;
	}
}

Zend_Auth_Adapter with multiple identity columns (username and email)

I’ve just started working with the Zend Framework and I must say it’s awesome. So powerful and so many helpers in place to make things easy to do. I’ve just been working with an authentication adapter. I wanted my users to be able to login using either their username or email address and a password. Zend_Auth_Adapter_DbTable does a brilliant job of using one field (email or username) but does not allow you to use both.

To do this I’ve written an auth adapter which has allowed me to use both. It’s a very simple class that adds an additional where clause option with the additional field. See below for the code…

 

class ZendExt_Auth_Adapter_MultiColumnDbTable 
              extends Zend_Auth_Adapter_DbTable
{
  protected $_alternativeIdentityColumn = null;
 
  protected function _authenticateCreateSelect()
  {
    $select = parent::_authenticateCreateSelect();
 
    if(isset($this->_alternativeIdentityColumn))
    {
      $select->orWhere($this->_zendDb->quoteIdentifier(
                      $this->_alternativeIdentityColumn, true) . ' = ?', 
                      $this->_identity);
    }
 
    return $select;
  }
 
  public function setAlternativeIdentityColumn($alternativeIdentityColumn)
  {
    $this->_alternativeIdentityColumn = $alternativeIdentityColumn;
    return $this;
  }
}

Pretty simple really. The important line is the “orWhere” bit which adds the additional column you want to use.
To use it you just have to do the following…

$dbAdapter = Zend_Db_Table::getDefaultAdapter();
$authAdapter = new ZendExt_Auth_Adapter_MultiColumnDbTable($dbAdapter);
$authAdapter->setTableName('user_user')
  ->setIdentityColumn('username')
  ->setAlternativeIdentityColumn('email')
  ->setCredentialColumn('password')
  ->setCredentialTreatment('MD5(?)');

No Virtual Mahines were found on this server

Right… This has been driving me crazy today. Not really sure why it happened but I’ve managed to sort if out now and wanted to share!

Our hyper-v server hosting our domain controller and a couple of Linux machines decided today that it didn’t want to admit to having any machines hosted on it. When opening hte hyper-v management tools on my Win 7 laptop I was getting the message “No Virtual Mahines were found on this server”. That was quite a worry and made me reluctant to re-boot the host machine through fear of not being able to control the hosted machines any more.

My first hurdle was getting access to the Hyper-v R2 Event Log. For some reason the firewall was not configured to allow me to access this. This was a simple fix of opening the fiewall on the hyper-v server to allow WMI access. The alternative is to drop the fire wall for the duration of this fix (netsh firewall set opmode disable) and re-enable it after (netsh firewall set opmode enable).

Ok the real crux of the problem. For some reason the security permissions on the hyper-v configuration files had been lost or reset. It took me an age to fix this but finally managed to piece together enough information to fix it.

The Virtual Machine Management Service uses some configuration files from the directory %SYSTEMDRIVE%\ProgramData\Microsoft\Windows\Hyper-V\Virtual Machines\. The xml files in this directory are in fact symlinks and should be named the same as the xml configuration files for the virtual machines them selves.

We store our virtual machines in the directory v:\VMs. So for example our machine test-01 has it’s xml file stored as V:\VMs\test-01\Virtual Machines\<guid>.xml.

Each virtual machine on the host has a similar set up. To fix the permissions you need to recreate the symlinks in the directory %SYSTEMDRIVE%\ProgramData\Microsoft\Windows\Hyper-V\Virtual Machines\. To do that you need to execute the following commands for each machine.

mklink “%SYSTEMDRIVE%\ProgramData\Microsoft\Windows\Hyper-V\Virtual Machines\<vm_guid>.xml” “V:\VMs\win2k8r2\Virtual Machines\<vm_guid>.xml”

icacls “%SYSTEMDRIVE%\ProgramData\Microsoft\Windows\Hyper-V\Virtual Machines\<vm_guid>.xml” /grant “NT VIRTUAL MACHINE\<vm_guid>”:F /l

The one part that I missed initially was the “/l” on the end of the icacls command. This is very important and tells icacls to apply the security permission to the link rather than the destination file.

If any of your VM’s have snapshots you need to repeat a similar procedure for the snapshots directory.

mklink “%SYSTEMDRIVE%\ProgramData\Microsoft\Windows\Hyper-V\Snapshots\<snapshot_guid>.xml” “V:\VMs\test-01\Snapshots\<snapshot_guid>.xml”

icacls “%SYSTEMDRIVE%\ProgramData\Microsoft\Windows\Hyper-V\Snapshots\<snapshot_guid>.xml” /grant “NT VIRTUAL MACHINE\<vm_guid>”:F /l

When you have repaired all the files in the directories simply restart the Virtual Machine Management Service” by using the following two commands:

net stop vmms

net start vmms

You should now be able to view the files in the Hyper-v management console.

Burn Down Charts

I’ve been considering the value of a burn down chart for measuring the progress of an Agile project. We are using DSDM Atern at work and as a consultancy company we like to try to use the techniques we recommend to others in house before preaching the benefits of them to others. I realise that Burn Down charts originated in Scrum but I would propose that Scrum does not have enough of a robust framework to be of any real value as a complete Project Management framework.

My question is: Are Burn Down charts a realistic progress indicator for a timebox?

My reason for this questioning is that when we used the burn down chart in house we discovered that at any point within a timebox we had some requirements completed others in progress and others not started. With this situation the completed tasks are indicated accurately on the burn down, as are the incomplete tasks.

The grey area is around the tasks that are in progress. Realistically the tasks that are in progress have not been completed so should therefor not be burnt down on the chart. It has however had work completed upon it so should you burn down the work to date on the task? It gives a misrepresentation of the status of the progress. If a task is not complete it’s not been delivered. If near the end of completing a task a sizeable problem occurs meaning the task will take much longer to complete than expected the burnt down progress becomes inaccurate.

My thoughts on this are that we do not burn down any progress on the burn down chart until a task is completed. That way we have a accurate view of what has been completed. I think it needs to be understood when doing this that the chart only displays 100% completed tasks and does not cover in anyway the in progress work.

The other inaccuracy is in relation to the prioritisation of Requirements. When we talk about DSDM Atern we are aware that we need to remember that it is very possible that some requirements will get de-prioritised and not completed to allow the project to stay on track. With this in mind then the bottom of the burn down chart which sits at “Zero Story Points” remaining is not an accurate view. Should you consider the base line of the chart to be the point at which all “Must Have” requirements have been completed and then any of the “Should Have” and “Could Have” requirements are added bonuses?

The thoughts behind this are that in theory the Timebox has not breached the plan if all the must have requirements are completed.

As a reporting tool to management the view of what the chart is showing should be made very clear. The chart could be used to display the information that fits with the requirements of the person creating it. If management are not aware of what is included and what is being displayed it can be very misleading.

I’m interested in what peoples usages are of burn down charts so add a comment to the post start a discussion.

Training Complete

It’s Wednesday and I’ve completed my first training course. I’ve just been running a Business Analysis Essentials course for TCC. It’s the first course that I’ve run end to end and it went very successfully. The course covers several aspects of Business Analysis from high level strategic positioning through to Business Activity Modelling. Hopefully this will lead to running more courses.

Sandbach Bound

Tonight I’m heading up North for 2 days with work. It’s a two part trip really (at least for me!).

Part 1 is to sit an Agile Project Management Practitioner exam. Should be good and am feeling not too bad about it.

Part 2 is to collect my shiny new work laptop. I’ve got a HP Pavilion DV7-4040sa waiting for me at headquarters! I’ll post some pictures and some feedback on what I think of the machine. It’s going to replace my work PC so I will truly be a mobile worker now. I’ve got a few accessories arriving over the next few days as well (laptop bag, desktop power cable) so I should hopefully have a complete laptop set-up very soon.

Desktop Power Plugs

Has anyone seen the Masterplug desktop power things? I’ve just purchased 8 for our company. Some users already had some but as we are now fitting out a new office and the need for a desktop power solution arose I thought I’d search for more… it appears that SVP have a good price on these at the moment. Try one for your self! They’re a plug socket, USB Hub, Audio outlet and network adapter with surge protection built in! Very good value at just over a fiver each.

PHP xdiff (libxdiff) on Windows

Not this is a tricky little bugger to get working. Xdiff is a fantastic set of difference libraries for obtaining differences between files or strings. The installation on Linux was covered in a previous post but Windows was a completely different ball game. I’ve finally managed to get it working however… so here we go.

Process Overview

To install this you need to compile a php_xdiff.dll file. To do this you will need to obtain Visual Studio 2008 (a.k.a VC9). This opens up several problems however. To run the compiled xdiff dll file you will need to have a VC9 version of php installed. If you install a VC9 version of PHP and use apache you will need a VC9 version of Apache. See where the problems start?!!!

PHP

There is no getting away from the fact that you are going to require a VC9 version of PHP if you compile a php_xdiff.dll file. VC6 is not available any more and it makes sense to compile this with the most updated version available. This is reasonably easily fixed as PHP offer a VC9 compiled version of PHP (http://windows.php.net/download/)

Apache

This is reasonably easily fixed as well. The guys at ApacheLounge compile VC9 versions of Apache and make them readily available. See http://www.apachelounge.com/download/.

Compiling php_xdiff.dll

This is the more troublesome stretch of the process. There is a good guide available at http://wiki.php.net/internals/windows/stepbystepbuild for getting started. If you follow this up to the point of adding extensions you should be on good ground. One word of advice would be to download all the extensions headers and libraries available. Just make sure you have everything available. If you’ve managed to compile a version of PHP with no extensions added you’re a good way there!

When you’ve got the PHP build available it’s now time to start working on xdiff. First of all you need to obtain and compile libxdiff. The source can be obtained from http://www.xmailserver.org/xdiff-lib.html. Once you have this you need to compile it using the visual studio tools. If you’ve still got your compiler window open from compiling PHP, change to the directly where you have libxdiff extracted to and run “nmake”. This should compile some headers and library files. Once compiled you need to copy all the headers and libraries into the appropriate PHP library directories.

From here you need to follow the steps at the bottom of the PHP internals page regarding installing additional PECL extensions. This should then compile. As a note. When you give the additional “configure” command use “–with-xdiff=shared”. This will cause the library to be built as a dll file rather than compiled into php5ts.dll file.

Putting it all together

Finally you need to add the dll to the php install you insatlled earlier. Copy the dll file to the “ext” directory under the php install (with the other extensions). Edit php.ini and add the line “extension=php_xdiff.dll” into the extensions section.

Finally restart apache and you should now have xdiff functionality within PHP. If you’ve managed this… give your self a pat on the back!

UPDATE:

I’ve uploaded the xdiff file that I compiled. If you want to use this simply install VC9 versions of PHP and Apache and place this file into the ext directory, edit the php.ini file and you’re away. It’s available here: php_xdiff.

PHP xdiff installation in Linux

Ok… I’ve been trying to get this working for several days and have finally made it work. XDiff is a brilliant diffing utility for PHP but requires you to install it as an extension. Before you are able to do the main part of this you need to install the libxdiff libraries. This is the part that took me some time to get working.

Ok here is the part that has taken me the time… libxdiff. To install this you need to do the following:
cd /usr/src
wget http://www.xmailserver.org/libxdiff-0.22.tar.gz
tar -xzf libxdiff-0.22.tar.gz
cd libxdiff-0.22
./configure
make
make install

This will get you libxdiff installed. The next part is then simple. Install the php extension using the following:
pecl install xdiff

When you have this installed you should then be able to use xdiff from within PHP. See the php manual for more instructions: http://www.php.net/manual/en/ref.xdiff.php