Browse Source

coding standards and fixes

euromark 13 years ago
parent
commit
efd30c948d

+ 173 - 0
View/Helper/BootstrapHelper.php

@@ -0,0 +1,173 @@
+<?php
+
+/**
+ * Generic html snippets to display some specific widgets like accordion
+ * 
+ * Note: 
+ * This is meant to work with TwitterBootstrap (Layout and Script), but 
+ * should also work with other custom solutions (if they use the same selectors).
+ * 
+ * Dependencies:
+ * The respective JS plugins for the widgets.
+ * 
+ * @license MIT
+ * @author Mark Scherer
+ * @cakephp 2.0
+ * @php 5
+ * @version 1.0
+ * 2012-03-29 ms
+ */
+class BootstrapHelper extends AppHelper {
+	
+	public $helpers = array('Html', 'Form');
+
+	protected $_count = 1;
+	
+	protected $_items = array();
+
+
+	public function xx() {
+		//
+	}
+
+	/**
+	 * complete typeahead form input element
+	 * 
+	 * @param fieldName
+	 * @param options:
+	 * - data (array of strings)
+	 * - items (defaults to 8)
+	 * @return string $html
+	 * 2012-03-29 ms
+	 */
+	public function typeahead($fieldName, $options = array(), $inputOptions = array()) {
+		$inputOptions['data-provide'] = 'typeahead';
+		//$inputOptions['data-source'] = $this->_formatSource($options['data']);
+		if (!empty($options['items'])) {
+			$inputOptions['data-items'] = (int)$options['items'];
+		}
+		$class = 'typeahead_'.strtolower(Inflector::slug($fieldName)); // str_replace('.', '_', $fieldName);
+		$inputOptions['class'] = empty($inputOptions['class']) ? $class : $inputOptions['class'].' '.$class;
+		
+		$script = '
+	$(\'.'.$class.'\').typeahead({
+		source: '.$this->_formatSource($options['data']).'
+	})
+';
+		$script = PHP_EOL.'<script>'.$script.'</script>';
+		return $this->Form->input($fieldName, $inputOptions) . $script;
+	}
+	
+	public function _formatSource($elements) {
+		//$res = array();
+		//return '[\''.implode('\',\'', $elements).'\']';
+		return json_encode($elements);
+	}
+
+	/**
+	 * complete carousel container
+	 * 
+	 * @param array $items (heading, content, active)
+	 * @param id
+	 * @param array $options
+	 * @return string $html
+	 * 2012-03-29 ms
+	 */
+	public function carousel($items, $id = null, $globalOptions = array()) {
+		$res = '<div id="myCarousel" class="carousel">
+  <div class="carousel-inner">
+  	'.$this->carouselItems($items, $globalOptions).'
+  </div>
+	'.$this->carouselControl().' 
+</div>';
+    return $res;
+	}
+	
+	public function carouselControl() {
+		$res = '<a class="carousel-control left" href="#myCarousel" data-slide="prev">&lsaquo;</a>
+    <a class="carousel-control right" href="#myCarousel" data-slide="next">&rsaquo;</a>';
+		return $res;
+	}
+	
+	/**
+	 * items of a carousel container
+	 * 
+	 * @param array $items (heading, content, active)
+	 * - active (visible, true/false)
+	 * @return string $html
+	 * 2012-03-29 ms
+	 */
+	public function carouselItems($items, $options = array()) {
+		$res = array();
+		foreach ($items as $key => $item) {
+			$active = '';
+			if ($key == 0 && !isset($item['active']) || !empty($item['active'])) {
+				$active = ' active';
+			}
+			$tmp = $item['content'];
+			if (!empty($item['heading'])) {
+				$tmp .= '<div class="carousel-caption">'.$item['heading'].'</div>';
+			}
+			$tmp = '<div class="item'.$active.'">'.$tmp.'</div>';
+			$res[] = $tmp;
+ 		}
+ 		$res = implode(PHP_EOL, $res);
+ 		return $res;
+	}
+
+	/**
+	 * complete accordion container
+	 * 
+	 * @param array $records (heading, content, options)
+	 * @param id
+	 * @param array $options
+	 * @return string $html
+	 * 2012-03-29 ms
+	 */
+	public function accordion($records, $id = null, $globalOptions = array()) {
+		$res = '<div class="accordion" id="accordion'.$id.'">';
+		foreach ($records as $record) {
+			$options = $globalOptions;
+			extract($record);
+			$res .= $this->accordionGroup($heading, $content, $options);
+		}
+		$res .= '</div>';
+		return $res;
+	}
+	
+	/**
+	 * a single group of an accordion container
+	 * 
+	 * @param string $heading
+	 * @param string $content
+	 * @param array $options
+	 * - active (collapsed, true/false)
+	 * @return string $html
+	 * 2012-03-29 ms
+	 */
+	public function accordionGroup($heading, $content, $options = array()) {
+		$i = $this->_count;
+		$this->_count++;
+		$in = '';
+		if ($i == 1 && !isset($options['active']) || !empty($options['active'])) {
+			$in = ' in';
+		}
+		
+		$res = '<div class="accordion-group">';
+		$res .= '	<div class="accordion-heading">';
+	
+    $res .= '		<a class="accordion-toggle" data-toggle="collapse" data-parent="#accordion" href="#collapse'.$i.'">';
+    $res .= $heading;
+    $res .= '		</a>';
+  	$res .= '	</div>';
+  	$res .= '	<div id="collapse'.$i.'" class="accordion-body collapse'.$in.'">';
+    $res .= '	<div class="accordion-inner">';
+    $res .= $content;
+  	$res .= '	</div>';
+ 		$res .= '	</div>';
+		$res .= '</div>';
+		return $res;
+	}
+	
+}
+

+ 9 - 9
View/Helper/CommonHelper.php

@@ -67,7 +67,7 @@ class CommonHelper extends AppHelper {
 		if (!empty($language)) {
 			$options['lang'] = mb_strtolower($language);
 		} elseif ($language !== false) {
-			$options['lang'] = Configure::read('Settings.locale'); // DEFAULT_LANGUAGE
+			$options['lang'] = Configure::read('Config.locale'); // DEFAULT_LANGUAGE
 		}
 		return $this->Html->meta('description', $content, $options);
 	}
@@ -85,7 +85,7 @@ class CommonHelper extends AppHelper {
 		if (!empty($language)) {
 			$options['lang'] = mb_strtolower($language);
 		} elseif ($language !== false) {
-			$options['lang'] = Configure::read('Settings.locale'); // DEFAULT_LANGUAGE
+			$options['lang'] = Configure::read('Config.locale'); // DEFAULT_LANGUAGE
 		}
 		return $this->Html->meta('keywords', $keywords, $options);
 	}
@@ -194,7 +194,7 @@ class CommonHelper extends AppHelper {
 		foreach ($files as $file) {
 			$pieces[] = 'file='.$file;
 		}
-		if ($v = Configure::read('Settings.layout_v')) {
+		if ($v = Configure::read('Config.layout_v')) {
 			$pieces[] = 'v='.$v;
 		}
 		$string = implode('&', $pieces);
@@ -213,7 +213,7 @@ class CommonHelper extends AppHelper {
 		foreach ($files as $file) {
 			$pieces[] = 'file='.$file;
 		}
-		if ($v = Configure::read('Settings.layout_v')) {
+		if ($v = Configure::read('Config.layout_v')) {
 			$pieces[] = 'v='.$v;
 		}
 		$string = implode('&', $pieces);
@@ -350,7 +350,7 @@ class CommonHelper extends AppHelper {
 	public function asp($s, $c, $autoTranslate = false) {
 		if ((int)$c != 1) {
 			$p = Inflector::pluralize($s);
-		} else{
+		} else {
 		 		$p = null; # no pluralization neccessary
 		}
 		return $this->sp($s, $p, $c, $autoTranslate);
@@ -364,7 +364,7 @@ class CommonHelper extends AppHelper {
 	public function sp($s, $p, $c, $autoTranslate = false) {
 		if ((int)$c != 1) {
 			$ret = $p;
-		} else{
+		} else {
 		 		$ret = $s;
 		}
 
@@ -832,8 +832,8 @@ if ($.browser.msie) {
 		if (!defined('HTTP_HOST_LIVESERVER')) {
 			return '';
 		}
-		if (HTTP_HOST == HTTP_HOST_LIVESERVER && (int)Configure::read('Settings.tracking') === 1) {
-			$trackingUrl = Configure::read('Settings.tracking_url');
+		if (HTTP_HOST == HTTP_HOST_LIVESERVER && (int)Configure::read('Config.tracking') === 1) {
+			$trackingUrl = Configure::read('Config.tracking_url');
 			if (empty($trackingUrl)) {
 				$trackingUrl = 'visit_stats';
 			}
@@ -869,7 +869,7 @@ piwikTracker.enableLinkTracking();
 	 */
 	public function visitStatsImg($trackingUrl = null) {
 		if (empty($trackingUrl)) {
-			$trackingUrl = Configure::read('Settings.tracking_url');
+			$trackingUrl = Configure::read('Config.tracking_url');
 		}
 		if (empty($trackingUrl)) {
 			$trackingUrl = 'visit_stats';

+ 18 - 6
View/Helper/DatetimeHelper.php

@@ -3,7 +3,8 @@
 App::uses('DatetimeLib', 'Tools.Lib');
 App::uses('TimeHelper', 'View/Helper');
 
-class DatetimeHelper extends TimeHelper { // later: App
+class DatetimeHelper extends TimeHelper {
+	
 	public $helpers = array('Html');
 
 	public $Datetime;
@@ -32,7 +33,6 @@ class DatetimeHelper extends TimeHelper { // later: App
 			//trigger_error(__('Magic method handler call__ not defined in %s', get_class($this)), E_USER_ERROR);
 		}
 		return call_user_func_array(array($this->Datetime, $method), $params);
-		//return $this->Datetime->$method($params);
 	}
 
 
@@ -133,10 +133,10 @@ class DatetimeHelper extends TimeHelper { // later: App
 			if ($this->isToday($date)) {
 				$when = 0;
 				$niceDate = __('Today').$timeAttachment;
-			} elseif($this->isTomorrow($date)) {
+			} elseif ($this->isTomorrow($date)) {
 				$when = 1;
 				$niceDate = __('Tomorrow').$timeAttachment;
-			} elseif($this->wasYesterday($date)) {
+			} elseif ($this->wasYesterday($date)) {
 				$when = -1;
 				$niceDate = __('Yesterday').$timeAttachment;
 			} else {
@@ -169,8 +169,20 @@ class DatetimeHelper extends TimeHelper { // later: App
 			//$span = '<span class="published '..'">';	// -1/-2 = ago | 1/2 = ahead | 0 = today
 			//$spanEnd = '</span>';
 		}
-		return $this->Html->tag('span',$niceDate,$attr);
-		//return $span.$nice_date.$spanEnd;
+		if (isset($this->Html)) {
+			return $this->Html->tag('span', $niceDate, $attr);
+		}
+		$a = array();
+		foreach ($attr as $key => $val) {
+			$a[] = $key.'="'.$val.'"';
+		}
+		$attr = '';
+		if (!empty($a)) {
+			$attr .= ' '.implode(' ', $a);
+		}
+		$span = '<span'.$attr.'>';
+		$spanEnd = '</span>';
+		return $span.$niceDate.$spanEnd;
 	}
 
 

+ 47 - 4
View/Helper/FormExtHelper.php

@@ -45,6 +45,12 @@ class FormExtHelper extends FormHelper { // Maybe FormHelper itself some day?
 		return parent::_introspectModel($model, $key, $field);
 	}
 
+	public function postLink($title, $url = null, $options = array(), $confirmMessage = false) {
+		if (!isset($options['class'])) {
+			$options['class'] = 'postLink';
+		}
+		return parent::postLink($title, $url ,$options, $confirmMessage);
+	}
 
 
 /**
@@ -141,11 +147,10 @@ class FormExtHelper extends FormHelper { // Maybe FormHelper itself some day?
 			(!isset($options['options']) && in_array($options['type'], $types)) ||
 			(isset($magicType) && $options['type'] == 'text')
 		) {
-			$view = ClassRegistry::getObject('view');
 			$varName = Inflector::variable(
 				Inflector::pluralize(preg_replace('/_id$/', '', $fieldKey))
 			);
-			$varOptions = $view->getVar($varName);
+			$varOptions = $this->_View->getVar($varName);
 			if (is_array($varOptions)) {
 				if ($options['type'] !== 'radio') {
 					$options['type'] = 'select';
@@ -333,6 +338,7 @@ class FormExtHelper extends FormHelper { // Maybe FormHelper itself some day?
 		}
 		
 		if (isset($options['required'])) {
+			$this->_introspectModel($modelKey, 'validates', $fieldKey);
 			$this->fieldset[$modelKey]['validates'][$fieldKey] = $options['required'];
 			if ($options['required'] === false) {
 				$autoRequire = Configure::read('Validation.autoRequire');
@@ -779,7 +785,7 @@ class FormExtHelper extends FormHelper { // Maybe FormHelper itself some day?
 
 	public function maxLengthScripts() {
 		if (!$this->scriptsAdded['maxLength']) {
-			$this->Html->script('jquery/maxlength/jquery.maxlength', false);
+			$this->Html->script('jquery/maxlength/jquery.maxlength', array('inline'=>false));
 			$this->scriptsAdded['maxLength'] = true;
 		}
 	}
@@ -818,6 +824,43 @@ class FormExtHelper extends FormHelper { // Maybe FormHelper itself some day?
 jQuery(\''.$selector.'\').maxlength('.$this->Js->object($settings, array('quoteKeys'=>false)).');
 ';
 	}
+	
+	
+	public function scripts($type) {
+		switch ($type) {
+			case 'charCount':
+				$this->Html->script('jquery/plugins/charCount', array('inline'=>false));
+				$this->Html->css('/js/jquery/plugins/charCount', null, array('inline'=>false));
+				break;
+			default:
+				return false;
+		}
+		$this->scriptsAdded[$type] = true;
+		return true;
+	}
+	
+	
+	public $charCountOptions = array(
+		'allowed' => 255,
+	);
+	
+	public function charCount($selectors = array(), $options = array()) {
+		$this->scripts('charCount');
+		$js = '';
+		
+		$selectors = (array)$selectors;
+		foreach ($selectors as $selector => $settings) {
+			if (is_int($selector)) {
+				$selector = $settings;
+				$settings = array();
+			}
+			$settings = am($this->charCountOptions, $options, $settings);
+			$js .= 'jQuery(\''.$selector.'\').charCount('.$this->Js->object($settings, array('quoteKeys'=>false)).');';
+		}
+		$js = $this->documentReady($js);
+		return $this->Html->scriptBlock($js);
+	}
+	
 
 
 	public function documentReady($string) {
@@ -938,7 +981,7 @@ jQuery(\''.$selector.'\').maxlength('.$this->Js->object($settings, array('quoteK
 /** other stuff **/
 
 	/**
-	 * echo $formExt->buttons($buttons);
+	 * echo $this->FormExt->buttons($buttons);
 	 * with
 	 * $buttons = array(
 	 *  array(

+ 7 - 9
View/Helper/FormatHelper.php

@@ -489,8 +489,7 @@ class FormatHelper extends TextHelper {
 
 		//$encmail = '&#109;a&#105;&#108;t&#111;&#58;'.$encMail;
 		$querystring = '';
-		foreach ($params as $key=>$val)
-		{
+		foreach ($params as $key=>$val) {
 			if ($querystring) {
 				$querystring .= "&$key=".rawurlencode($val);
 			} else {
@@ -538,8 +537,7 @@ class FormatHelper extends TextHelper {
 	 */
 	public function encodeText($text) {
 		$encmail = '';
-		for ($i=0; $i<mb_strlen($text); $i++)
-		{
+		for ($i=0; $i<mb_strlen($text); $i++) {
 			$encMod = mt_rand(0,2);
 			switch ($encMod) {
 			case 0: // None
@@ -571,7 +569,7 @@ class FormatHelper extends TextHelper {
 		$sbez = array('0'=>@substr($offtitle, 0, 1), '1'=>@substr($ontitle, 0, 1));
 		$bez = array('0'=>$offtitle, '1'=>$ontitle);
 
-		if ($v==$on) {$icon=ICON_YES; $value=1;}else{$icon=ICON_NO; $value=0;}
+		if ($v==$on) {$icon=ICON_YES; $value=1;} else {$icon=ICON_NO; $value=0;}
 
 		if ($text!==false) {
 			$light=$bez[$value];
@@ -907,7 +905,7 @@ class FormatHelper extends TextHelper {
 			$res .= '<div class="highslide-heading">' . $title . '</div>';
 		}
 
-		$navigation = '<div class="floatRight">&nbsp;' . $this->Html->defaultLink($this->cIcon(ICON_ALBUM, 'Zum zugehörigen Album wechseln'),
+		$navigation = '<div class="floatRight">&nbsp;' . $this->Html->defaultLink($this->cIcon(ICON_ALBUM, 'Zum Album wechseln'),
 			array('controller' => 'albums', 'action' => 'view', $image['Album']['id'], slug($image['Album']['title'])), array('escape' => false)) .
 			'</div>';
 		if (!empty($image['User']['id'])) {
@@ -947,7 +945,7 @@ class FormatHelper extends TextHelper {
 	 */
 	public function profileLink($uid, $username, $text = null, $attr = array(), $options = array()) {
 		if (empty($username)) {
-			return '[Account gelöscht]';
+			return '['.__('%s deleted', __('Account')).']';
 		}
 		$title = isset($text) ? $text : $username;
 		$username = slug($username);
@@ -1092,7 +1090,7 @@ class FormatHelper extends TextHelper {
 			$res = $icons[1];
 		}
 
-		return $this->Html->image('misc/' . $res, array('width' => '60', 'height' => '16'));
+		return $this->Html->image('misc/' . $res, array('style' => 'width: 60px; height: 16px'));
 	}
 
 	/**
@@ -1313,7 +1311,7 @@ class FormatHelper extends TextHelper {
 		$str = ' ' . $str . ' ';
 
 		// \w, \b and a few others do not match on a unicode character
-		// set for performance reasons. As a result words like über
+		// set for performance reasons. As a result words like ..ber
 		// will not match on a word boundary. Instead, we'll assume that
 		// a bad word will be bookended by any of these characters.
 		$delim = '[-_\'\"`() {}<>\[\]|!?@#%&,.:;^~*+=\/ 0-9\n\r\t]';

+ 98 - 0
View/Helper/HcardHelper.php

@@ -0,0 +1,98 @@
+<?php
+
+/**
+ * helper for hcard, vcard and other microformats!
+ *
+ * TODO: import http://codeigniter.com/wiki/microformats/
+ * 2011-01-11 ms
+ */
+class HcardHelper extends AppHelper {
+
+	public $data = array (
+	'given_name' => 'Firstname',
+	'middle_name' => 'Middlename',
+	'family_name' => 'Lastname',
+	'organization' => 'OrganizationName',
+	'street' => '123 Street',
+	'city' => 'City',
+	'province' => 'Province/State',
+	'postal_code' => 'Postal/Zip',
+	'country' => 'Country',
+	'phone' => 'phonenumber',
+	'email' => 'email@yoursite.com',
+	'url' => 'http://yoursite.com',
+	'aim_screenname' => 'aimname',
+	'yim_screenname' => 'yimname',
+	'avatar' => '/images/your_photo.png',
+	'title' => 'title',
+	);
+
+
+	/**
+	 * TODO
+	 */
+	public function addressFormatHtml($data,  $prefix = false, $format = 'General') {
+		$data = $this->filter($data, $prefix);
+		$text = $this->style($data, $format);
+		$text = '';
+		$text .= '<div id="hcard-' . $data['firstname'] . '-' . $data['lastname'] . '" class="vcard">';
+		$text .= '<span class="fn">' . $data['firstname'] . ' ' . $data['lastname'] . '</span>';
+		$text .= $this->address($data, $format);
+		$text .= '</div>';
+		return $text;
+	}
+
+	/**
+	 * TODO
+	 */
+	public function addressFormatRaw($data,  $prefix = false, $format = 'General') {
+		$data = $this->filter($data, $prefix);
+		$text = $data['firstname'] . ' ' . $data['lastname'] . "\n";
+		$text .= $data['address'] . "\n";
+		if (Configure::read('Localization.address_format') == 'US') {
+			$text .= $data['city'] . ', ' . $data['state'] . ' ' . $data['postcode'] . "\n";
+		} else {
+			$text .= $data['postcode'] . ' ' . $data['city'] . "\n";
+		}
+		$text .= $data['country'];
+		return $text;
+	}
+
+	/**
+	 * TODO
+	 */
+	public function style($data) {
+	}
+
+	/**
+	 * TODO
+	 */
+	public function address($data) {
+		$text = '<div class="adr">';
+		$text .= '<div class="street-address">' . $data['address'] . '</div> ';
+		$text .= '<span class="locality">' . $data['city'] . '</span>, ';
+		if (!empty($data['state'])) {
+			$text .= '<span class="region">' . $data['state'] . '</span> ';
+		}
+		$text .= '<span class="postal-code">' . $data['postcode'] . '</span> ';
+		$text .= '<span class="country-name">' . $data['country'] . '</span> ';
+		$text .= '</div>';
+		return $text;
+
+	}
+
+
+	/**
+	 * TODO
+	 */
+	public function filter($data, $prefix = '') {
+		if ($prefix) {
+			foreach ($data as $key => $row) {
+				$data[$prefix . $key] = $data[$key];
+			}
+		}
+		return $data;
+	}
+
+}
+

+ 62 - 0
View/Helper/IcalHelper.php

@@ -0,0 +1,62 @@
+<?php
+
+App::uses('Helper', 'View');
+
+/**
+ * uses ical lib
+ * tipps see http://labs.iamkoa.net/2007/09/07/create-downloadable-ical-events-via-cake/
+ * 
+ * needs ical layout
+ * needs Router::parseExtensions('ics') in router.php
+ * 
+ * 2011-10-10 ms
+ */
+class IcalHelper extends AppHelper {
+	
+	public $helpers = array(); //'Html'
+
+	public $Ical;
+
+	protected $_data = array();
+
+	
+	public function __construct($View = null, $settings = array()) {
+		parent::__construct($View, $settings);
+		
+		App::uses('IcalLib', 'Tools.Lib');
+		$this->Ical = new IcalLib();
+	}
+
+
+	public function reset() {
+		$this->$_data = array();
+	}
+
+	/**
+	 * add a new ical record
+	 * @return boolean $success
+	 */
+	public function add($data = array()) {
+		//TODO: validate!
+		$this->_data[] = $data;
+		
+		return true;
+	}
+
+	/**
+	 * returns complete ical calender file content to output
+	 * 2011-10-10 ms
+	 */
+	public function generate($globalData = array(), $addStartAndEnd = true) {
+		$res = array();
+		foreach ($this->_data as $data) {
+			$res[] = $this->Ical->build($data);
+		}
+		$res = implode(PHP_EOL.PHP_EOL, $res);
+		if ($addStartAndEnd) {
+			$res = $this->Ical->createStart($globalData).PHP_EOL.PHP_EOL.$res.PHP_EOL.PHP_EOL.$this->Ical->createEnd();
+		}
+		return $res;
+	}
+
+}

+ 8 - 1
View/Helper/NumericHelper.php

@@ -66,7 +66,10 @@ class NumericHelper extends NumberHelper {
 		} elseif ((float)$price > 0 || !empty($formatOptions['allowNegative'])) {
 			$val = $price;
 		} else {
-			return '---';
+			if (isset($formatOptions['default'])) {
+				return $formatOptions['default'];
+			}
+			$val = $price;
 		}
 
 		if ($places === null) {
@@ -154,4 +157,8 @@ class NumericHelper extends NumberHelper {
 		}
 	}
 
+
+	public function currency($number, $currency = 'EUR', $options = array()) {
+		return parent::currency($number, $currency, $options);
+	}
 }

+ 36 - 36
View/Helper/PhpThumbHelper.php

@@ -9,7 +9,7 @@
  */
 class PhpThumbHelper extends AppHelper    {
 
-	protected $php_thumb;
+	protected $PhpThumb;
 	protected $options;
 	protected $file_extension;
 	protected $cache_filename;
@@ -17,52 +17,52 @@ class PhpThumbHelper extends AppHelper    {
 	protected $error;
 	protected $error_detail;
 
-	protected function init($options = array())    {
+	protected function init($options = array()) {
 		$this->options = $options;
 		$this->set_file_extension();
 		$this->thumb_data = array();
 		$this->error = 0;
 	}
 
-	protected function set_file_extension()    {
+	protected function set_file_extension() {
 		$this->file_extension = mb_substr($this->options['src'], mb_strrpos($this->options['src'], '.'), mb_strlen($this->options['src']));
 	}
 
-	protected function set_cache_filename()    {
+	protected function set_cache_filename() {
 		ksort($this->options);
 		$filename_parts = array();
 		$cacheable_properties = array('src', 'new', 'w', 'h', 'wp', 'hp', 'wl', 'hl', 'ws', 'hs', 'f', 'q', 'sx', 'sy', 'sw', 'sh', 'zc', 'bc', 'bg', 'fltr');
 
-		foreach ($this->options as $key => $value)    {
-			if (in_array($key, $cacheable_properties))    {
+		foreach ($this->options as $key => $value) {
+			if (in_array($key, $cacheable_properties)) {
 				$filename_parts[$key] = $value;
 			}
 		}
 
 		$this->cache_filename = '';
 
-		foreach ($filename_parts as $key => $value)    {
+		foreach ($filename_parts as $key => $value) {
 			$this->cache_filename .= $key . $value;
 		}
 
-		$last_modified = date("F d Y H:i:s.", filectime($this->options['src']));
+		$last_modified = ''; //date("F d Y H:i:s.", filectime($this->options['src']));
 
 		$this->cache_filename = $this->options['save_path'] . DS . md5($this->cache_filename . $last_modified) . $this->file_extension;
 	}
 
-	protected function image_is_cached()    {
-		if (is_file($this->cache_filename))    {
+	protected function image_is_cached() {
+		if (is_file($this->cache_filename)) {
 			return true;
 		}
 		return false;
 	}
 
-	protected function create_thumb()    {
-		if (!isset($this->php_thumb) || !is_object($this->php_thumb)) {
+	protected function create_thumb() {
+		if (!isset($this->PhpThumb) || !is_object($this->PhpThumb)) {
 			App::import('Vendor', 'phpThumb', array('file' => 'phpThumb'.DS.'phpthumb.class.php'));
-			}
-			$this->php_thumb = new phpThumb();
-			set_time_limit(30);
+		}
+		$this->PhpThumb = new PhpThumb();
+		set_time_limit(30);
 
 		//TODO: make it cleaner
 		# addon
@@ -82,48 +82,48 @@ class PhpThumbHelper extends AppHelper    {
 			if (!empty($PHPTHUMB_CONFIG)) {
 				foreach ($PHPTHUMB_CONFIG as $key => $value) {
 					$keyname = 'config_'.$key;
-					$this->php_thumb->setParameter($keyname, $value);
+					$this->PhpThumb->setParameter($keyname, $value);
 				}
 			}
 			# addon end
 
-		foreach ($this->php_thumb as $var => $value) {
-			if (isset($this->options[$var]))    {
-				$this->php_thumb->setParameter($var, $this->options[$var]);
+		foreach ($this->PhpThumb as $var => $value) {
+			if (isset($this->options[$var])) {
+				$this->PhpThumb->setParameter($var, $this->options[$var]);
 			}
 		}
 
-		if ($this->php_thumb->GenerateThumbnail()) {
-			$this->php_thumb->RenderToFile($this->cache_filename);
+		if ($this->PhpThumb->GenerateThumbnail()) {
+			$this->PhpThumb->RenderToFile($this->cache_filename);
 		} else {
 			$this->error = 1;
-			$this->error_detail = ereg_replace("[^A-Za-z0-9\/: .]", "", $this->php_thumb->fatalerror);
+			$this->error_detail = ereg_replace("[^A-Za-z0-9\/: .]", "", $this->PhpThumb->fatalerror);
 		}
 	}
 
-	protected function get_thumb_data()    {
+	protected function get_thumb_data() {
 		$this->thumb_data['error'] = $this->error;
 
-		if ($this->error)    {
+		if ($this->error) {
 			$this->thumb_data['error_detail'] = $this->error_detail;
 			$this->thumb_data['src'] = $this->options['error_image_path'];
-		} else    {
+		} else {
 			$this->thumb_data['src'] = $this->options['display_path'] . '/' . mb_substr($this->cache_filename, mb_strrpos($this->cache_filename, DS) + 1, mb_strlen($this->cache_filename));
 		}
 
-		if (isset($this->options['w']))    {
+		if (isset($this->options['w'])) {
 			$this->thumb_data['w'] = $this->options['w'];
 		}
 
-		if (isset($this->options['h']))    {
+		if (isset($this->options['h'])) {
 			 $this->thumb_data['h'] = $this->options['h'];
 		}
 
 		return $this->thumb_data;
 	}
 
-	protected function validate()	{
-		if (!is_file($this->options['src']))	{
+	protected function validate() {
+		if (!is_file($this->options['src'])) {
 			$this->error = 1;
 			$this->error_detail = 'File ' . $this->options['src'] . ' does not exist';
 			return;
@@ -131,21 +131,21 @@ class PhpThumbHelper extends AppHelper    {
 
 		$valid_extensions = array('.gif', '.jpg', '.jpeg', '.png');
 
-		if (!in_array($this->file_extension, $valid_extensions))	{
+		if (!in_array($this->file_extension, $valid_extensions)) {
 			$this->error = 1;
 			$this->error_detail = 'File ' . $this->options['src'] . ' is not a supported image type';
 			return;
 		}
 	}
 
-	public function generate($options = array())    {
+	public function generate($options = array()) {
 		$this->init($options);
 
 		$this->validate();
 
 		if (!$this->error) {
 			$this->set_cache_filename();
-			if (!$this->image_is_cached())    {
+			if (!$this->image_is_cached()) {
 				$this->create_thumb();
 			}
 		}
@@ -169,9 +169,9 @@ class PhpThumbHelper extends AppHelper    {
 	 */
 	public function show($options = array(), $tag_options = array()) {
 		$this->init($options, $tag_options);
-		if ($this->image_is_cached())	{
+		if ($this->image_is_cached()) {
 			return $this->show_image_tag();
-		} else	{
+		} else {
 			$this->create_thumb();
 			return $this->show_image_tag();
 		}
@@ -182,9 +182,9 @@ class PhpThumbHelper extends AppHelper    {
 	 */
 	public function show_src($options = array()) {
 		$this->init($options);
-		if ($this->image_is_cached())	{
+		if ($this->image_is_cached()) {
 			return $this->get_image_src();
-		} else	{
+		} else {
 			$this->create_thumb();
 			return $this->get_image_src();
 		}

+ 344 - 0
View/Helper/TextExtHelper.php

@@ -0,0 +1,344 @@
+<?php
+
+App::uses('TextHelper', 'View/Helper');
+App::uses('HtmlHelper', 'View/Helper');
+App::uses('View', 'View');
+
+/**
+ * the core text helper is unsecure and outdated in functionality
+ * this aims to compensate the deficiencies
+ * 
+ * autoLinkEmails
+ * - obfuscate (defaults to FALSE right now)
+ * (- maxLength?)
+ * - escape (defaults to TRUE for security reasons regarding plain text)
+ * 
+ * autoLinkUrls
+ * - stripProtocol (defaults To FALSE right now) 
+ * - maxLength (to shorten links in order to not mess up the layout in some cases - appends ...)
+ * - escape (defaults to TRUE for security reasons regarding plain text)
+ *   
+ * 2011-03-30 ms
+ */
+class TextExtHelper extends TextHelper { 
+
+	/**
+	 * Convert all links and email adresses to HTML links.
+	 *
+	 * @param string $text Text
+	 * @param array $options Array of HTML options.
+	 * @return string The text with links
+	 * @access public
+	 * @link http://book.cakephp.org/view/1469/Text#autoLink-1620
+	 */
+	public function autoLink($text, $options = array(), $htmlOptions = array()) {
+		if (!isset($options['escape']) || $options['escape'] !== false) {
+			$text = h($text);
+			$options['escape'] = false;
+		}
+		return $this->autoLinkEmails($this->autoLinkUrls($text, $options, $htmlOptions), $options, $htmlOptions);
+	}
+	
+
+	/**
+	 * fix to allow obfuscation of email (js, img?)
+	 * @param string $text
+	 * @param htmlOptions (additionally - not yet supported by core):
+	 * - obfuscate: true/false (defaults to false)
+	 * @param array $options
+	 * - escape (defaults to true)
+	 * @return string $html
+	 * @override
+	 * 2010-11-20 ms
+	 */
+	public function autoLinkEmails($text, $options = array(), $htmlOptions = array()) {
+		if (!isset($options['escape']) || $options['escape'] !== false) {
+			$text = h($text);
+		}
+		
+		$linkOptions = 'array(';
+		foreach ($htmlOptions as $option => $value) {
+			$value = var_export($value, true);
+			$linkOptions .= "'$option' => $value, ";
+		}
+		$linkOptions .= ')';
+		
+		$customOptions = 'array(';
+		foreach ($options as $option => $value) {
+			$value = var_export($value, true);
+			$customOptions .= "'$option' => $value, ";
+		}
+		$customOptions .= ')';
+		
+		$atom = '[a-z0-9!#$%&\'*+\/=?^_`{|}~-]';
+		return preg_replace_callback('/(' . $atom . '+(?:\.' . $atom . '+)*@[a-z0-9-]+(?:\.[a-z0-9-]+)+)/i',
+						create_function('$matches', 'return TextExtHelper::prepareEmail($matches[0],' . $linkOptions . ',' . $customOptions . ');'), $text);
+	}
+
+	/**
+	 * @param string $email
+	 * @param options:
+	 * - obfuscate: true/false (defaults to false)
+	 * @return string $html
+	 * @static
+	 * 2010-11-20 ms
+	 */
+	public function prepareEmail($email, $options = array(), $customOptions = array()) {
+		$obfuscate = false;
+		if (isset($options['obfuscate'])) {
+			$obfuscate = $options['obfuscate'];
+			unset($options['obfuscate']);
+		}
+		
+		if (!isset($customOptions['escape']) || $customOptions['escape'] !== false) {
+			$email = hDec($email);
+		}
+		
+		$Html = new HtmlHelper(new View(null)); 
+		//$Html->tags = $Html->loadConfig(); 
+		//debug($Html->tags);
+		if (!$obfuscate) { 
+			return $Html->link($email, "mailto:" . $email, $options);
+		}
+		
+		$class = __CLASS__;
+		$Common = new $class;
+		$Common->Html = $Html;
+		return $Common->encodeEmailUrl($email, null, array(), $options); 
+	}
+
+
+	/**
+	 * Helper Function to Obfuscate Email by inserting a span tag (not more! not very secure on its own...)
+	 * each part of this mail now does not make sense anymore on its own
+	 * (striptags will not work either)
+	 * @param string email: neccessary (and valid - containing one @)
+	 * @return string $html
+	 * 2009-03-11 ms
+	 */
+	public function encodeEmail($mail) {
+		list($mail1, $mail2) = explode('@', $mail);
+		$encMail = $this->encodeText($mail1).'<span>@</span>'.$this->encodeText($mail2);
+		return $encMail;
+	}
+
+	/**
+	 * Obfuscates Email (works without JS!) to avoid lowlevel spam bots to get it
+	 * @param string mail: email to encode
+	 * @param string text: optional (if none is given, email will be text as well)
+	 * @param array attributes: html tag attributes
+	 * @param array params: ?subject=y&body=y to be attached to "mailto:xyz"
+	 * @return string $html with js generated link around email (and non js fallback)
+	 * 2009-04-20 ms
+	 */
+	public function encodeEmailUrl($mail, $text=null, $params=array(), $attr = array()) {
+		if (empty($class)) { $class='email'; }
+
+		$defaults = array(
+			'title' => __('for use in an external mail client'),
+			'class' => 'email',
+			'escape' => false
+		);
+
+		if (empty($text)) {
+			$text = $this->encodeEmail($mail);
+		}
+
+		$encMail = 'mailto:'.$mail;
+		//$encMail = $this->encodeText($encMail); # not possible
+		// additionally there could be a span tag in between: email<span syle="display:none"></span>@web.de
+
+		$querystring = '';
+		foreach ($params as $key => $val) {
+			if ($querystring) {
+				$querystring .= "&$key=".rawurlencode($val);
+			} else {
+				$querystring = "?$key=".rawurlencode($val);
+			}
+		}
+
+		$attr = array_merge($defaults, $attr);
+
+
+		$xmail = $this->Html->link('', $encMail.$querystring, $attr);
+		$xmail1 = mb_substr($xmail, 0, count($xmail)-5);
+		$xmail2 = mb_substr($xmail, -4, 4);
+
+		$len = mb_strlen($xmail1);
+		$i=0;
+		while ($i<$len) {
+			$c = mt_rand(2,6);
+			$par[] = (mb_substr($xmail1, $i, $c));
+			$i += $c;
+		}
+		$join = implode('\'+\'', $par);
+	
+			return '<script language=javascript><!--
+		document.write(\''.$join.'\');
+		//--></script>
+			'.$text.'
+		<script language=javascript><!--
+		document.write(\''.$xmail2.'\');
+		//--></script>';
+
+		//return '<a class="'.$class.'" title="'.$title.'" href="'.$encmail.$querystring.'">'.$encText.'</a>';
+	}
+
+
+	/**
+	 * Encodes Piece of Text (without usage of JS!) to avoid lowlevel spam bots to get it
+	 * @param STRING text to encode
+	 * @return string $html (randomly encoded)
+	 * 2009-03-11 ms
+	 */
+	public function encodeText($text) {
+		$encmail = '';
+		for ($i=0; $i < mb_strlen($text); $i++) {
+			$encMod = mt_rand(0,2);
+			switch ($encMod) {
+			case 0: // None
+				$encmail .= mb_substr($text, $i, 1);
+				break;
+			case 1: // Decimal
+				$encmail .= "&#".ord(mb_substr($text, $i, 1)).';';
+				break;
+			case 2: // Hexadecimal
+				$encmail .= "&#x".dechex(ord(mb_substr($text, $i, 1))).';';
+				break;
+			}
+		}
+		return $encmail;
+	}
+
+
+	/**
+	 * fix to allow shortened urls that do not break layout etc
+	 * @param string $text
+	 * @param options (additionally - not yet supported by core):
+	 * - stripProtocol: bool (defaults to true)
+	 * - maxLength: int (defaults no none)
+	 * @param htmlOptions 
+	 * - escape etc
+	 * @return string $html
+	 * @override
+	 * 2010-11-07 ms
+	 */
+	public function autoLinkUrls($text, $options = array(), $htmlOptions = array()) {
+		if (!isset($options['escape']) || $options['escape'] !== false) {
+			$text = h($text);
+			$matchString = 'hDec($matches[0])';
+		} else {
+			$matchString = '$matches[0]';
+		}
+		
+		if (isset($htmlOptions['escape'])) {
+			$options['escape'] = $htmlOptions['escape'];
+		}
+		//$htmlOptions['escape'] = false;
+						
+		$htmlOptions = var_export($htmlOptions, true);
+		$customOptions = var_export($options, true);
+		
+		$text = preg_replace_callback('#(?<!href="|">)((?:https?|ftp|nntp)://[^\s<>()]+)#i', create_function('$matches',
+			'$Html = new HtmlHelper(new View(null)); return $Html->link(TextExtHelper::prepareLinkName(hDec($matches[0]), '.$customOptions.'), hDec($matches[0]),' . $htmlOptions . ');'), $text);
+
+		return preg_replace_callback('#(?<!href="|">)(?<!http://|https://|ftp://|nntp://)(www\.[^\n\%\ <]+[^<\n\%\,\.\ <])(?<!\))#i',
+			create_function('$matches', '$Html = new HtmlHelper(new View(null)); return $Html->link(TextExtHelper::prepareLinkName(hDec($matches[0]), '.$customOptions.'), "http://" . hDec($matches[0]),' . $htmlOptions . ');'), $text);
+	}
+
+	/**
+	 * @param string $link
+	 * @param options:
+	 * - stripProtocol: bool (defaults to true)
+	 * - maxLength: int (defaults to 50)
+	 * - escape (defaults to false, true needed for hellip to work)
+	 * @return string $html/$plain
+	 * @static
+	 * 2010-11-07 ms
+	 */
+	public function prepareLinkName($link, $options = array()) {
+		# strip protocol if desired (default)
+		if (!isset($options['stripProtocol']) || $options['stripProtocol'] !== false) {
+			$link = self::stripProtocol($link);
+		}
+		if (!isset($options['maxLength'])) {
+			$options['maxLength'] = 50; # should be long enough for most cases
+		}
+		# shorten display name if desired (default)
+		if (!empty($options['maxLength']) && mb_strlen($link) > $options['maxLength']) {
+			$link = mb_substr($link, 0, $options['maxLength']);
+			# problematic with autoLink()
+			if (!empty($options['html']) && isset($options['escape']) && $options['escape'] === false) {
+				$link .= '&hellip;'; # only possible with escape => false!
+			} else {
+				$link .= '...';
+			}
+		}
+		return $link;
+	}
+
+	/**
+	 * remove http:// or other protocols from the link
+	 * @param string $url
+	 * @return string $strippedUrl
+	 * @static
+	 * 2010-11-07 ms
+	 */
+	public function stripProtocol($url) {
+		$pieces = parse_url($url);
+		if (empty($pieces['scheme'])) {
+			return $url; # already stripped
+		}
+		return mb_substr($url, mb_strlen($pieces['scheme'])+3); # +3 <=> :// # can only be 4 with "file" (file:///)...
+	}
+
+	/**
+	 * Transforming int values into ordinal numbers (1st, 3rd, etc.)
+	 * @param $num (INT) - the number to be suffixed.
+	 * @param $sup (BOOL) - whether to wrap the suffix in a superscript (<sup>) tag on output.
+	 * @return string $ordinal
+	 */
+	public function ordinalNumber($num = 0, $sup = false) {
+		$suff = '';
+		if (!in_array(($num % 100), array(11, 12, 13))) {
+			switch ($num % 10) {
+				case 1:
+					$suff = 'st';
+					break;
+				case 2:
+					$suff = 'nd';
+					break;
+				case 3:  
+					$suff = 'rd';
+					break;
+				default: 
+					$suff = 'th';
+			}
+		}
+		return ($sup) ? $num . '<sup>' . $suff . '</sup>' : $num . $suff;
+	}
+		
+
+	/**
+	 * syntax highlighting using php internal highlighting
+	 * @param string $filename
+	 * @param bool $return (else echo directly)
+	 * 2009-07-26 ms
+	 */
+	public function highlightFile($file, $return = true) {
+		return highlight_file($file, $return);
+	}
+
+	/**
+	 * syntax highlighting using php internal highlighting
+	 * @param string $contentstring
+	 * @param bool $return (else echo directly)
+	 * 2009-07-26 ms
+	 */
+	public function highlightString($string, $return = true) {
+		return highlight_string($string, $return);
+	}
+
+}
+
+

+ 447 - 0
View/Helper/TreeHelper.php

@@ -0,0 +1,447 @@
+<?php
+	/* SVN FILE: $Id: tree.php 205 2008-08-13 14:13:32Z ad7six $ */
+/**
+ * Tree Helper.
+ *
+ * Used the generate nested representations of hierarchial data
+ *
+ * PHP versions 4 and 5
+ *
+ * Copyright (c) 2008, Andy Dawson
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright            Copyright (c) 2008, Andy Dawson
+ * @link                 www.ad7six.com
+ * @package              cake-base
+ * @subpackage           cake-base.app.views.helpers
+ * @since                v 1.0
+ * @version              $Revision: 205 $
+ * @modifiedBy           $LastChangedBy: ad7six $
+ * @lastModified         $Date: 2008-08-13 16:13:32 +0200 (Wed, 13 Aug 2008) $
+ * @license              http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+/**
+ * Tree helper
+ *
+ * Helper to generate tree representations of MPTT or recursively nested data
+ */
+class TreeHelper extends AppHelper {
+
+/**
+ * settings property
+ *
+ * @var array
+ * @access private
+ */
+	public $_settings = array();
+/**
+ * typeAttributes property
+ *
+ * @var array
+ * @access private
+ */
+	public $_typeAttributes = array();
+/**
+ * typeAttributesNext property
+ *
+ * @var array
+ * @access private
+ */
+	public $_typeAttributesNext = array();
+/**
+ * itemAttributes property
+ *
+ * @var array
+ * @access private
+ */
+	public $_itemAttributes = array();
+/**
+ * helpers variable
+ *
+ * @var array
+ * @access public
+ */
+	public $helpers = array ('Html');
+/**
+ * Tree generation method.
+ *
+ * Accepts the results of
+ * 	find('all', array('fields' => array('lft', 'rght', 'whatever'), 'order' => 'lft ASC'));
+ * 	children(); // if you have the tree behavior of course!
+ * or 	findAllThreaded(); and generates a tree structure of the data.
+ *
+ * Settings (2nd parameter):
+ *	'model' => name of the model (key) to look for in the data array. defaults to the first model for the current
+ * controller. If set to false 2d arrays will be allowed/expected.
+ *	'alias' => the array key to output for a simple ul (not used if element or callback is specified)
+ *	'type' => type of output defaults to ul
+ *	'itemType => type of item output default to li
+ *	'id' => id for top level 'type'
+ *	'class' => class for top level 'type'
+ *	'element' => path to an element to render to get node contents.
+ *	'callback' => callback to use to get node contents. e.g. array(&$anObject, 'methodName') or 'floatingMethod'
+ *	'autoPath' =>  array($left, $right [$classToAdd = 'active']) if set any item in the path will have the class $classToAdd added. MPTT only.
+ *	'left' => name of the 'lft' field if not lft. only applies to MPTT data
+ *	'right' => name of the 'rght' field if not lft. only applies to MPTT data
+ *	'depth' => used internally when running recursively, can be used to override the depth in either mode.
+ *	'firstChild' => used internally when running recursively.
+ *	'splitDepth' => if multiple "parallel" types are required, instead of one big type, nominate the depth to do so here
+ *		example: useful if you have 30 items to display, and you'd prefer they appeared in the source as 3 lists of 10 to be able to
+ *		style/float them.
+ *	'splitCount' => the number of "parallel" types. defaults to 3
+ *
+ * @param array $data data to loop on
+ * @param array $settings
+ * @return string html representation of the passed data
+ * @access public
+ */
+	public function generate($data, $settings = array ()) {
+		$this->_settings = array_merge(array(
+				'model' => null,
+				'alias' => 'name',
+				'type' => 'ul',
+				'itemType' => 'li',
+				'id' => false,
+				'class' => false,
+				'element' => false,
+				'callback' => false,
+				'autoPath' => false,
+				'left' => 'lft',
+				'right' => 'rght',
+				'depth' => 0,
+				'firstChild' => true,
+				'indent' => null,
+				'splitDepth' => false,
+				'splitCount' => 3,
+			), (array)$settings);
+		if ($this->_settings['autoPath'] && !isset($this->_settings['autoPath'][2])) {
+			$this->_settings['autoPath'][2] = 'active';
+		}
+		extract($this->_settings);
+		if ($indent === null && Configure::read('debug')) {
+			$indent = true;
+		}
+		if ($model === null) {
+			//$model = Inflector::classify($this->_View->params['models'][0]);
+		}
+		if (!$model) {
+			$model = '_NULL_';
+		}
+		$stack = array();
+		if ($depth == 0) {
+			if ($class) {
+				$this->addTypeAttribute('class', $class, null, 'previous');
+			}
+			if ($id) {
+				$this->addTypeAttribute('id', $id, null, 'previous');
+			}
+		}
+		$return = '';
+		if ($indent) {
+			$return = "\r\n";
+		}
+		$_addType = true;
+		foreach ($data as $i => $result) {
+			/* Allow 2d data arrays */
+			if ($model == '_NULL_') {
+				$_result = $result;
+				$result[$model] = $_result;
+			}
+			/* BulletProof */
+			if (!isset($result[$model][$left]) && !isset($result['children'])) {
+				$result['children'] = array();
+			}
+			/* Close open items as appropriate */
+			while ($stack && ($stack[count($stack)-1] < $result[$model][$right])) {
+				array_pop($stack);
+				if ($indent) {
+					$whiteSpace = str_repeat("\t",count($stack));
+					$return .= "\r\n" . $whiteSpace . "\t";
+				}
+				if ($type) {
+					$return .= '</' . $type . '>';
+				}
+				if ($itemType) {
+					$return .= '</' . $itemType . '>';
+				}
+			}
+			/* Some useful vars */
+			$hasChildren = $firstChild = $lastChild = $hasVisibleChildren = false;
+			$numberOfDirectChildren = $numberOfTotalChildren = null;
+			if (isset($result['children'])) {
+				if ($result['children']) {
+					$hasChildren = $hasVisibleChildren = true;
+					$numberOfDirectChildren = count($result['children']);
+				}
+				$prevRow = prev($data);
+				if (!$prevRow) {
+					$firstChild = true;
+				}
+				next($data);
+				$nextRow = next($data);
+				if (!$nextRow) {
+					$lastChild = true;
+				}
+				prev($data);
+			} elseif (isset($result[$model][$left])) {
+				if ($result[$model][$left] != ($result[$model][$right] - 1)) {
+					$hasChildren = true;
+					$numberOfTotalChildren = ($result[$model][$right] - $result[$model][$left] - 1) / 2;
+					if (isset($data[$i + 1]) && $data[$i + 1][$model][$right] < $result[$model][$right]) {
+						$hasVisibleChildren = true;
+					}
+				}
+				if (!isset($data[$i - 1]) || ($data[$i - 1][$model][$left] == ($result[$model][$left] - 1))) {
+					$firstChild = true;
+				}
+				if (!isset($data[$i + 1]) || ($stack && $stack[count($stack) - 1] == ($result[$model][$right] + 1))) {
+					$lastChild = true;
+				}
+			}
+			$elementData = array(
+				'data' => $result,
+				'depth' => $depth?$depth:count($stack),
+				'hasChildren' => $hasChildren,
+				'numberOfDirectChildren' => $numberOfDirectChildren,
+				'numberOfTotalChildren' => $numberOfTotalChildren,
+				'firstChild' => $firstChild,
+				'lastChild' => $lastChild,
+				'hasVisibleChildren' => $hasVisibleChildren
+			);
+			$this->_settings = array_merge($this->_settings, $elementData);
+			/* Main Content */
+			if ($element) {
+				$content = $this->_View->element($element, $elementData);
+			} elseif ($callback) {
+				list($content) = array_map($callback, array($elementData));
+			} else {
+				$content = $result[$model][$alias];
+			}
+			if (!$content) {
+				continue;
+			}
+			$whiteSpace = str_repeat("\t", $depth);
+			if ($indent && strpos($content, "\r\n", 1)) {
+				$content = str_replace("\r\n", "\n" . $whiteSpace . "\t", $content);
+			}
+			/* Prefix */
+			if ($_addType) {
+				if ($indent) {
+					$return .= "\r\n" . $whiteSpace;
+				}
+				if ($type) {
+					$typeAttributes = $this->_attributes($type, array('data' => $elementData));
+					$return .= '<' . $type .  $typeAttributes . '>';
+				}
+			}
+			if ($indent) {
+				$return .= "\r\n" . $whiteSpace . "\t";
+			}
+			if ($itemType) {
+				$itemAttributes = $this->_attributes($itemType, $elementData);
+				$return .= '<' . $itemType . $itemAttributes . '>';
+			}
+			$return .= $content;
+			/* Suffix */
+			$_addType = false;
+			if ($hasVisibleChildren) {
+				if ($numberOfDirectChildren) {
+					$settings['depth'] = $depth + 1;
+					$return .= $this->_suffix();
+					$return .= $this->generate($result['children'], $settings);
+					if ($itemType) {
+						$return .= '</' . $itemType . '>';
+					}
+				} elseif ($numberOfTotalChildren) {
+					$_addType = true;
+					$stack[] = $result[$model][$right];
+				}
+			} else {
+				if ($itemType) {
+					$return .= '</' . $itemType . '>';
+				}
+				$return .= $this->_suffix();
+			}
+		}
+		/* Cleanup */
+		while ($stack) {
+			array_pop($stack);
+			if ($indent) {
+				$whiteSpace = str_repeat("\t",count($stack));
+				$return .= "\r\n" . $whiteSpace . "\t";
+			}
+			if ($type) {
+				$return .= '</' . $type . '>';
+			}
+			if ($itemType) {
+				$return .= '</' . $itemType . '>';
+			}
+		}
+		if ($indent) {
+			$return .= "\r\n";
+		}
+		if ($type) {
+			$return .= '</' . $type . '>';
+			if ($indent) {
+				$return .= "\r\n";
+			}
+		}
+		return $return;
+	}
+/**
+ * addItemAttribute function
+ *
+ * Called to modify the attributes of the next <item> to be processed
+ * Note that the content of a 'node' is processed before generating its wrapping <item> tag
+ *
+ * @param string $id
+ * @param string $key
+ * @param mixed $value
+ * @access public
+ * @return void
+ */
+	public function addItemAttribute($id = '', $key = '', $value = null) {
+		if (!is_null($value)) {
+			$this->_itemAttributes[$id][$key] = $value;
+		} elseif (!(isset($this->_itemAttributes[$id]) && in_array($key, $this->_itemAttributes[$id]))) {
+			$this->_itemAttributes[$id][] = $key;
+		}
+	}
+/**
+ * addTypeAttribute function
+ *
+ * Called to modify the attributes of the next <type> to be processed
+ * Note that the content of a 'node' is processed before generating its wrapping <type> tag (if appropriate)
+ * An 'interesting' case is that of a first child with children. To generate the output
+ * <ul> (1)
+ *      <li>XYZ (3)
+ *              <ul> (2)
+ *                      <li>ABC...
+ *                      ...
+ *              </ul>
+ *              ...
+ * The processing order is indicated by the numbers in brackets.
+ * attributes are allways applied to the next type (2) to be generated
+ * to set properties of the holding type - pass 'previous' for the 4th param
+ * i.e.
+ * // Hide children (2)
+ * $this->Tree->addTypeAttribute('style', 'display', 'hidden');
+ * // give top level type (1) a class
+ * $this->Tree->addTypeAttribute('class', 'hasHiddenGrandChildren', null, 'previous');
+ *
+ * @param string $id
+ * @param string $key
+ * @param mixed $value
+ * @access public
+ * @return void
+ */
+	public function addTypeAttribute($id = '', $key = '', $value = null, $previousOrNext = 'next') {
+		$var = '__typeAttributes';
+		$firstChild = isset($this->_settings['firstChild'])?$this->_settings['firstChild']:true;
+		if ($previousOrNext == 'next' && $firstChild) {
+			$var = '__typeAttributesNext';
+		}
+		if (!is_null($value)) {
+			$this->{$var}[$id][$key] = $value;
+		} elseif (!(isset($this->{$var}[$id]) && in_array($key, $this->{$var}[$id]))) {
+			$this->{$var}[$id][] = $key;
+		}
+	}
+
+/**
+ * supressChildren method
+ *
+ * @return void
+ * @access public
+ */
+	public function supressChildren() {
+	}
+/**
+ * suffix method
+ *
+ * Used to close and reopen a ul/ol to allow easier listings
+ *
+ * @access private
+ * @return void
+ */
+	public function _suffix() {
+		static $_splitCount = 0;
+		static $_splitCounter = 0;
+		extract($this->_settings);
+		if ($splitDepth) {
+			if ($depth == $splitDepth -1) {
+				$total = $numberOfDirectChildren?$numberOfDirectChildren:$numberOfTotalChildren;
+				if ($total) {
+					$_splitCounter = 0;
+					$_splitCount = $total / $splitCount;
+					$rounded = (int)$_splitCount;
+					if ($rounded < $_splitCount) {
+						$_splitCount = $rounded + 1;
+					}
+				}
+			}
+			if ($depth == $splitDepth) {
+				$_splitCounter++;
+				if ($type && ($_splitCounter % $_splitCount) == 0) {
+					return '</' . $type . '><' . $type . '>';
+				}
+			}
+		}
+		return;
+	}
+/**
+ * attributes function
+ *
+ * Logic to apply styles to tags.
+ *
+ * @param mixed $rType
+ * @param array $elementData
+ * @access private
+ * @return void
+ */
+	public function _attributes($rType, $elementData = array(), $clear = true) {
+		extract($this->_settings);
+		if ($rType == $type) {
+			$attributes = $this->_typeAttributes;
+			if ($clear) {
+				$this->_typeAttributes = $this->_typeAttributesNext;
+				$this->_typeAttributesNext = array();
+			}
+		} else {
+			$attributes = $this->_itemAttributes;
+			$this->_itemAttributes = array();
+			if ($clear) {
+				$this->_itemAttributes = array();
+			}
+		}
+		if ($autoPath && $depth) {
+			if ($this->_settings['data'][$model][$left] < $autoPath[0] && $this->_settings['data'][$model][$right] > $autoPath[1]) {
+				$attributes['class'][] = $autoPath[2];
+			} elseif (isset($autoPath[3]) && $this->_settings['data'][$model][$left] == $autoPath[0]) {
+				$attributes['class'][] = $autoPath[3];
+			}
+		}
+		if ($attributes) {
+			foreach ($attributes as $type => $values) {
+				foreach ($values as $key => $val) {
+					if (is_array($val)) {
+						$attributes[$type][$key] = '';
+						foreach ($val as $vKey => $v) {
+							$attributes[$type][$key][$vKey] .= $vKey . ':' . $v;
+						}
+						$attributes[$type][$key] = implode(';', $attributes[$type][$key]);
+					}
+					if (is_string($key)) {
+						$attributes[$type][$key] = $key . ':' . $val . ';';
+					}
+				}
+				$attributes[$type] = $type . '="' . implode(' ', $attributes[$type]) . '"';
+			}
+			return ' ' . implode(' ', $attributes);
+		}
+		return '';
+	}
+}