PageRenderTime 33ms CodeModel.GetById 8ms app.highlight 15ms RepoModel.GetById 2ms app.codeStats 0ms

/cake/libs/view/helper.php

https://github.com/hardsshah/bookmarks
PHP | 753 lines | 413 code | 37 blank | 303 comment | 110 complexity | ac9f7c0147ff8d5d19d95a1741da8fcf MD5 | raw file
  1<?php
  2/* SVN FILE: $Id$ */
  3/**
  4 * Backend for helpers.
  5 *
  6 * Internal methods for the Helpers.
  7 *
  8 * PHP versions 4 and 5
  9 *
 10 * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
 11 * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
 12 *
 13 * Licensed under The MIT License
 14 * Redistributions of files must retain the above copyright notice.
 15 *
 16 * @filesource
 17 * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
 18 * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
 19 * @package       cake
 20 * @subpackage    cake.cake.libs.view
 21 * @since         CakePHP(tm) v 0.2.9
 22 * @version       $Revision$
 23 * @modifiedby    $LastChangedBy$
 24 * @lastmodified  $Date$
 25 * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
 26 */
 27/**
 28 * Included libs
 29 */
 30App::import('Core', 'Overloadable');
 31
 32/**
 33 * Backend for helpers.
 34 *
 35 * Long description for class
 36 *
 37 * @package       cake
 38 * @subpackage    cake.cake.libs.view
 39 */
 40class Helper extends Overloadable {
 41/**
 42 * List of helpers used by this helper
 43 *
 44 * @var array
 45 */
 46	var $helpers = null;
 47/**
 48 * Base URL
 49 *
 50 * @var string
 51 */
 52	var $base = null;
 53/**
 54 * Webroot path
 55 *
 56 * @var string
 57 */
 58	var $webroot = null;
 59/**
 60 * Theme name
 61 *
 62 * @var string
 63 */
 64	var $themeWeb = null;
 65/**
 66 * URL to current action.
 67 *
 68 * @var string
 69 */
 70	var $here = null;
 71/**
 72 * Parameter array.
 73 *
 74 * @var array
 75 */
 76	var $params = array();
 77/**
 78 * Current action.
 79 *
 80 * @var string
 81 */
 82	var $action = null;
 83/**
 84 * Plugin path
 85 *
 86 * @var string
 87 */
 88	var $plugin = null;
 89/**
 90 * POST data for models
 91 *
 92 * @var array
 93 */
 94	var $data = null;
 95/**
 96 * List of named arguments
 97 *
 98 * @var array
 99 */
100	var $namedArgs = null;
101/**
102 * URL argument separator character
103 *
104 * @var string
105 */
106	var $argSeparator = null;
107/**
108 * Contains model validation errors of form post-backs
109 *
110 * @access public
111 * @var array
112 */
113	var $validationErrors = null;
114/**
115 * Holds tag templates.
116 *
117 * @access public
118 * @var array
119 */
120	var $tags = array();
121/**
122 * Holds the content to be cleaned.
123 *
124 * @access private
125 * @var mixed
126 */
127	var $__tainted = null;
128/**
129 * Holds the cleaned content.
130 *
131 * @access private
132 * @var mixed
133 */
134	var $__cleaned = null;
135/**
136 * Default overload methods
137 *
138 * @access protected
139 */
140	function get__($name) {}
141	function set__($name, $value) {}
142	function call__($method, $params) {
143		trigger_error(sprintf(__('Method %1$s::%2$s does not exist', true), get_class($this), $method), E_USER_WARNING);
144	}
145
146/**
147 * Parses tag templates into $this->tags.
148 *
149 * @param $name file name
150 * @return array merged tags from config/$name.php
151 */
152	function loadConfig($name = 'tags') {
153		if (file_exists(CONFIGS . $name .'.php')) {
154			require(CONFIGS . $name .'.php');
155			if (isset($tags)) {
156				$this->tags = array_merge($this->tags, $tags);
157			}
158		}
159		return $this->tags;
160	}
161/**
162 * Finds URL for specified action.
163 *
164 * Returns an URL pointing to a combination of controller and action. Param
165 * $url can be:
166 *	+ Empty - the method will find adress to actuall controller/action.
167 *	+ '/' - the method will find base URL of application.
168 *	+ A combination of controller/action - the method will find url for it.
169 *
170 * @param  mixed  $url    Cake-relative URL, like "/products/edit/92" or "/presidents/elect/4"
171 *                        or an array specifying any of the following: 'controller', 'action',
172 *                        and/or 'plugin', in addition to named arguments (keyed array elements),
173 *                        and standard URL arguments (indexed array elements)
174 * @param boolean $full   If true, the full base URL will be prepended to the result
175 * @return string  Full translated URL with base path.
176 */
177	function url($url = null, $full = false) {
178		return h(Router::url($url, $full));
179	}
180/**
181 * Checks if a file exists when theme is used, if no file is found default location is returned
182 *
183 * @param  string  $file
184 * @return string  $webPath web path to file.
185 */
186	function webroot($file) {
187		$webPath = "{$this->webroot}" . $file;
188		if (!empty($this->themeWeb)) {
189			$os = env('OS');
190			if (!empty($os) && strpos($os, 'Windows') !== false) {
191				if (strpos(WWW_ROOT . $this->themeWeb  . $file, '\\') !== false) {
192					$path = str_replace('/', '\\', WWW_ROOT . $this->themeWeb  . $file);
193				}
194			} else {
195				$path = WWW_ROOT . $this->themeWeb  . $file;
196			}
197			if (file_exists($path)) {
198				$webPath = "{$this->webroot}" . $this->themeWeb . $file;
199			}
200		}
201		if (strpos($webPath, '//') !== false) {
202			return str_replace('//', '/', $webPath);
203		}
204		return $webPath;
205	}
206
207/**
208 * Used to remove harmful tags from content
209 *
210 * @param mixed $output
211 * @return cleaned content for output
212 * @access public
213 */
214	function clean($output) {
215		$this->__reset();
216		if (empty($output)) {
217			return null;
218		}
219		if (is_array($output)) {
220			foreach ($output as $key => $value) {
221				$return[$key] = $this->clean($value);
222			}
223			return $return;
224		}
225		$this->__tainted = $output;
226		$this->__clean();
227		return $this->__cleaned;
228	}
229/**
230 * Returns a space-delimited string with items of the $options array. If a
231 * key of $options array happens to be one of:
232 *	+ 'compact'
233 *	+ 'checked'
234 *	+ 'declare'
235 *	+ 'readonly'
236 *	+ 'disabled'
237 *	+ 'selected'
238 *	+ 'defer'
239 *	+ 'ismap'
240 *	+ 'nohref'
241 *	+ 'noshade'
242 *	+ 'nowrap'
243 *	+ 'multiple'
244 *	+ 'noresize'
245 *
246 * And its value is one of:
247 *	+ 1
248 *	+ true
249 *	+ 'true'
250 *
251 * Then the value will be reset to be identical with key's name.
252 * If the value is not one of these 3, the parameter is not output.
253 *
254 * @param  array  $options Array of options.
255 * @param  array  $exclude Array of options to be excluded.
256 * @param  string $insertBefore String to be inserted before options.
257 * @param  string $insertAfter  String to be inserted ater options.
258 * @return string
259 */
260	function _parseAttributes($options, $exclude = null, $insertBefore = ' ', $insertAfter = null) {
261		if (is_array($options)) {
262			$options = array_merge(array('escape' => true), $options);
263
264			if (!is_array($exclude)) {
265				$exclude = array();
266			}
267			$keys = array_diff(array_keys($options), array_merge((array)$exclude, array('escape')));
268			$values = array_intersect_key(array_values($options), $keys);
269			$escape = $options['escape'];
270			$attributes = array();
271
272			foreach ($keys as $index => $key) {
273				$attributes[] = $this->__formatAttribute($key, $values[$index], $escape);
274			}
275			$out = implode(' ', $attributes);
276		} else {
277			$out = $options;
278		}
279		return $out ? $insertBefore . $out . $insertAfter : '';
280	}
281/**
282 * @param  string $key
283 * @param  string $value
284 * @return string
285 * @access private
286 */
287	function __formatAttribute($key, $value, $escape = true) {
288		$attribute = '';
289		$attributeFormat = '%s="%s"';
290		$minimizedAttributes = array('compact', 'checked', 'declare', 'readonly', 'disabled', 'selected', 'defer', 'ismap', 'nohref', 'noshade', 'nowrap', 'multiple', 'noresize');
291		if (is_array($value)) {
292			$value = '';
293		}
294
295		if (in_array($key, $minimizedAttributes)) {
296			if ($value === 1 || $value === true || $value === 'true' || $value == $key) {
297				$attribute = sprintf($attributeFormat, $key, $key);
298			}
299		} else {
300			$attribute = sprintf($attributeFormat, $key, ($escape ? h($value) : $value));
301		}
302		return $attribute;
303	}
304/**
305 * Sets this helper's model and field properties to the dot-separated value-pair in $entity.
306 *
307 * @param mixed $entity A field name, like "ModelName.fieldName" or "ModelName.ID.fieldName"
308 * @param boolean $setScope Sets the view scope to the model specified in $tagValue
309 * @return void
310 */
311	function setEntity($entity, $setScope = false) {
312		$view =& ClassRegistry::getObject('view');
313
314		if ($setScope) {
315			$view->modelScope = false;
316		} elseif (join('.', $view->entity()) == $entity) {
317			return;
318		}
319
320		if ($entity === null) {
321			$view->model = null;
322			$view->association = null;
323			$view->modelId = null;
324			$view->modelScope = false;
325			return;
326		}
327
328		$model = $view->model;
329		$sameScope = $hasField = false;
330		$parts = array_values(Set::filter(explode('.', $entity), true));
331
332		if (empty($parts)) {
333			return;
334		}
335
336		if (count($parts) === 1 || is_numeric($parts[0])) {
337			$sameScope = true;
338		} else {
339			if (ClassRegistry::isKeySet($parts[0])) {
340				$model = $parts[0];
341			}
342		}
343
344		if (ClassRegistry::isKeySet($model)) {
345			$ModelObj =& ClassRegistry::getObject($model);
346			for ($i = 0; $i < count($parts); $i++) {
347				if ($ModelObj->hasField($parts[$i]) || array_key_exists($parts[$i], $ModelObj->validate)) {
348					$hasField = $i;
349					if ($hasField === 0 || ($hasField === 1 && is_numeric($parts[0]))) {
350						$sameScope = true;
351					}
352					break;
353				}
354			}
355
356			if ($sameScope === true && in_array($parts[0], array_keys($ModelObj->hasAndBelongsToMany))) {
357				$sameScope = false;
358			}
359		}
360
361		if (!$view->association && $parts[0] == $view->field && $view->field != $view->model) {
362			array_unshift($parts, $model);
363			$hasField = true;
364		}
365		$view->field = $view->modelId = $view->fieldSuffix = $view->association = null;
366
367		switch (count($parts)) {
368			case 1:
369				if ($view->modelScope === false) {
370					$view->model = $parts[0];
371				} else {
372					$view->field = $parts[0];
373					if ($sameScope === false) {
374						$view->association = $parts[0];
375					}
376				}
377			break;
378			case 2:
379				if ($view->modelScope === false) {
380					list($view->model, $view->field) = $parts;
381				} elseif ($sameScope === true && $hasField === 0) {
382					list($view->field, $view->fieldSuffix) = $parts;
383				} elseif ($sameScope === true && $hasField === 1) {
384					list($view->modelId, $view->field) = $parts;
385				} else {
386					list($view->association, $view->field) = $parts;
387				}
388			break;
389			case 3:
390				if ($sameScope === true && $hasField === 1) {
391					list($view->modelId, $view->field, $view->fieldSuffix) = $parts;
392				} elseif ($hasField === 2) {
393					list($view->association, $view->modelId, $view->field) = $parts;
394				} else {
395					list($view->association, $view->field, $view->fieldSuffix) = $parts;
396				}
397			break;
398			case 4:
399				if ($parts[0] === $view->model) {
400					list($view->model, $view->modelId, $view->field, $view->fieldSuffix) = $parts;
401				} else {
402					list($view->association, $view->modelId, $view->field, $view->fieldSuffix) = $parts;
403				}
404			break;
405		}
406
407		if (!isset($view->model) || empty($view->model)) {
408			$view->model = $view->association;
409			$view->association = null;
410		} elseif ($view->model === $view->association) {
411			$view->association = null;
412		}
413
414		if ($setScope) {
415			$view->modelScope = true;
416		}
417	}
418/**
419 * Gets the currently-used model of the rendering context.
420 *
421 * @return string
422 */
423	function model() {
424		$view =& ClassRegistry::getObject('view');
425		if (!empty($view->association)) {
426			return $view->association;
427		} else {
428			return $view->model;
429		}
430	}
431/**
432 * Gets the ID of the currently-used model of the rendering context.
433 *
434 * @return mixed
435 */
436	function modelID() {
437		$view =& ClassRegistry::getObject('view');
438		return $view->modelId;
439	}
440/**
441 * Gets the currently-used model field of the rendering context.
442 *
443 * @return string
444 */
445	function field() {
446		$view =& ClassRegistry::getObject('view');
447		return $view->field;
448	}
449/**
450 * Returns false if given FORM field has no errors. Otherwise it returns the constant set in the array Model->validationErrors.
451 *
452 * @param string $model		Model name as string
453 * @param string $field		Fieldname as string
454 * @param integer $modelID	Unique index identifying this record within the form
455 * @return boolean True on errors.
456 */
457	function tagIsInvalid($model = null, $field = null, $modelID = null) {
458		foreach (array('model', 'field', 'modelID') as $key) {
459			if (empty(${$key})) {
460				${$key} = $this->{$key}();
461			}
462		}
463		$view =& ClassRegistry::getObject('view');
464		$errors = $this->validationErrors;
465
466		if ($view->model !== $model && isset($errors[$view->model][$model])) {
467			$errors = $errors[$view->model];
468		}
469
470		if (!isset($modelID)) {
471			return empty($errors[$model][$field]) ? 0 : $errors[$model][$field];
472		} else {
473			return empty($errors[$model][$modelID][$field]) ? 0 : $errors[$model][$modelID][$field];
474		}
475	}
476/**
477 * Generates a DOM ID for the selected element, if one is not set.
478 *
479 * @param mixed $options
480 * @param string $id
481 * @return mixed
482 */
483	function domId($options = null, $id = 'id') {
484		$view =& ClassRegistry::getObject('view');
485
486		if (is_array($options) && array_key_exists($id, $options) && $options[$id] === null) {
487			unset($options[$id]);
488			return $options;
489		} elseif (!is_array($options) && $options !== null) {
490			$this->setEntity($options);
491			return $this->domId();
492		}
493
494		$dom = $this->model() . $this->modelID() . Inflector::camelize($view->field) . Inflector::camelize($view->fieldSuffix);
495
496		if (is_array($options) && !array_key_exists($id, $options)) {
497			$options[$id] = $dom;
498		} elseif ($options === null) {
499			return $dom;
500		}
501		return $options;
502	}
503/**
504 * Gets the input field name for the current tag
505 *
506 * @param array $options
507 * @param string $key
508 * @return array
509 */
510	function __name($options = array(), $field = null, $key = 'name') {
511		$view =& ClassRegistry::getObject('view');
512
513		if ($options === null) {
514			$options = array();
515		} elseif (is_string($options)) {
516			$field = $options;
517			$options = 0;
518		}
519
520		if (!empty($field)) {
521			$this->setEntity($field);
522		}
523
524		if (is_array($options) && array_key_exists($key, $options)) {
525			return $options;
526		}
527
528		switch ($field) {
529			case '_method':
530				$name = $field;
531			break;
532			default:
533				$name = 'data[' . join('][', $view->entity()) . ']';
534			break;
535		}
536
537		if (is_array($options)) {
538			$options[$key] = $name;
539			return $options;
540		} else {
541			return $name;
542		}
543	}
544/**
545 * Gets the data for the current tag
546 *
547 * @param array $options
548 * @param string $key
549 * @return array
550 * @access public
551 */
552	function value($options = array(), $field = null, $key = 'value') {
553		if ($options === null) {
554			$options = array();
555		} elseif (is_string($options)) {
556			$field = $options;
557			$options = 0;
558		}
559
560		if (!empty($field)) {
561			$this->setEntity($field);
562		}
563
564		if (is_array($options) && isset($options[$key])) {
565			return $options;
566		}
567
568		$result = null;
569
570		$modelName = $this->model();
571		$fieldName = $this->field();
572		$modelID = $this->modelID();
573
574		if (is_null($fieldName)) {
575			$fieldName = $modelName;
576			$modelName = null;
577		}
578
579		if (isset($this->data[$fieldName]) && $modelName === null) {
580			$result = $this->data[$fieldName];
581		} elseif (isset($this->data[$modelName][$fieldName])) {
582			$result = $this->data[$modelName][$fieldName];
583		} elseif (isset($this->data[$fieldName]) && is_array($this->data[$fieldName])) {
584			if (ClassRegistry::isKeySet($fieldName)) {
585				$model =& ClassRegistry::getObject($fieldName);
586				$result = $this->__selectedArray($this->data[$fieldName], $model->primaryKey);
587			}
588		} elseif (isset($this->data[$modelName][$modelID][$fieldName])) {
589			$result = $this->data[$modelName][$modelID][$fieldName];
590		}
591
592		if (is_array($result)) {
593			$view =& ClassRegistry::getObject('view');
594			if (isset($result[$view->fieldSuffix])) {
595				$result = $result[$view->fieldSuffix];
596			}
597		}
598
599		if (is_array($options)) {
600			if (empty($result) && isset($options['default'])) {
601				$result = $options['default'];
602			}
603			unset($options['default']);
604		}
605
606		if (is_array($options)) {
607			$options[$key] = $result;
608			return $options;
609		} else {
610			return $result;
611		}
612	}
613/**
614 * Sets the defaults for an input tag
615 *
616 * @param array $options
617 * @param string $key
618 * @return array
619 * @access protected
620 */
621	function _initInputField($field, $options = array()) {
622		if ($field !== null) {
623			$this->setEntity($field);
624		}
625		$options = (array)$options;
626		$options = $this->__name($options);
627		$options = $this->value($options);
628		$options = $this->domId($options);
629		if ($this->tagIsInvalid()) {
630			$options = $this->addClass($options, 'form-error');
631		}
632		return $options;
633	}
634/**
635 * Adds the given class to the element options
636 *
637 * @param array $options
638 * @param string $class
639 * @param string $key
640 * @return array
641 */
642	function addClass($options = array(), $class = null, $key = 'class') {
643		if (isset($options[$key]) && trim($options[$key]) != '') {
644			$options[$key] .= ' ' . $class;
645		} else {
646			$options[$key] = $class;
647		}
648		return $options;
649	}
650/**
651 * Returns a string generated by a helper method
652 *
653 * This method can be overridden in subclasses to do generalized output post-processing
654 *
655 * @param  string  $str	String to be output.
656 * @return string
657 */
658	function output($str) {
659		return $str;
660	}
661/**
662 * Before render callback.  Overridden in subclasses.
663 *
664 */
665	function beforeRender() {
666	}
667/**
668 * After render callback.  Overridden in subclasses.
669 *
670 */
671	function afterRender() {
672	}
673/**
674 * Before layout callback.  Overridden in subclasses.
675 *
676 */
677	function beforeLayout() {
678	}
679/**
680 * After layout callback.  Overridden in subclasses.
681 *
682 */
683	function afterLayout() {
684	}
685/**
686 * Transforms a recordset from a hasAndBelongsToMany association to a list of selected
687 * options for a multiple select element
688 *
689 * @param mixed $data
690 * @param string $key
691 * @return array
692 * @access private
693 */
694	function __selectedArray($data, $key = 'id') {
695		if (!is_array($data)) {
696			$model = $data;
697			if (!empty($this->data[$model][$model])) {
698				return $this->data[$model][$model];
699			}
700			if (!empty($this->data[$model])) {
701				$data = $this->data[$model];
702			}
703		}
704		$array = array();
705		if (!empty($data)) {
706			foreach ($data as $var) {
707				$array[$var[$key]] = $var[$key];
708			}
709		}
710		return $array;
711	}
712/**
713 * Resets the vars used by Helper::clean() to null
714 *
715 * @access private
716 */
717	function __reset() {
718		$this->__tainted = null;
719		$this->__cleaned = null;
720	}
721/**
722 * Removes harmful content from output
723 *
724 * @access private
725 */
726	function __clean() {
727		if (get_magic_quotes_gpc()) {
728			$this->__cleaned = stripslashes($this->__tainted);
729		} else {
730			$this->__cleaned = $this->__tainted;
731		}
732
733		$this->__cleaned = str_replace(array("&amp;", "&lt;", "&gt;"), array("&amp;amp;", "&amp;lt;", "&amp;gt;"), $this->__cleaned);
734		$this->__cleaned = preg_replace('#(&\#*\w+)[\x00-\x20]+;#u', "$1;", $this->__cleaned);
735		$this->__cleaned = preg_replace('#(&\#x*)([0-9A-F]+);*#iu', "$1$2;", $this->__cleaned);
736		$this->__cleaned = html_entity_decode($this->__cleaned, ENT_COMPAT, "UTF-8");
737		$this->__cleaned = preg_replace('#(<[^>]+[\x00-\x20\"\'\/])(on|xmlns)[^>]*>#iUu', "$1>", $this->__cleaned);
738		$this->__cleaned = preg_replace('#([a-z]*)[\x00-\x20]*=[\x00-\x20]*([\`\'\"]*)[\\x00-\x20]*j[\x00-\x20]*a[\x00-\x20]*v[\x00-\x20]*a[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iUu', '$1=$2nojavascript...', $this->__cleaned);
739		$this->__cleaned = preg_replace('#([a-z]*)[\x00-\x20]*=([\'\"]*)[\x00-\x20]*v[\x00-\x20]*b[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iUu', '$1=$2novbscript...', $this->__cleaned);
740		$this->__cleaned = preg_replace('#([a-z]*)[\x00-\x20]*=*([\'\"]*)[\x00-\x20]*-moz-binding[\x00-\x20]*:#iUu','$1=$2nomozbinding...', $this->__cleaned);
741		$this->__cleaned = preg_replace('#([a-z]*)[\x00-\x20]*=([\'\"]*)[\x00-\x20]*data[\x00-\x20]*:#Uu', '$1=$2nodata...', $this->__cleaned);
742		$this->__cleaned = preg_replace('#(<[^>]+)style[\x00-\x20]*=[\x00-\x20]*([\`\'\"]*).*expression[\x00-\x20]*\([^>]*>#iU', "$1>", $this->__cleaned);
743		$this->__cleaned = preg_replace('#(<[^>]+)style[\x00-\x20]*=[\x00-\x20]*([\`\'\"]*).*behaviour[\x00-\x20]*\([^>]*>#iU', "$1>", $this->__cleaned);
744		$this->__cleaned = preg_replace('#(<[^>]+)style[\x00-\x20]*=[\x00-\x20]*([\`\'\"]*).*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:*[^>]*>#iUu', "$1>", $this->__cleaned);
745		$this->__cleaned = preg_replace('#</*\w+:\w[^>]*>#i', "", $this->__cleaned);
746		do {
747			$oldstring = $this->__cleaned;
748			$this->__cleaned = preg_replace('#</*(applet|meta|xml|blink|link|style|script|embed|object|iframe|frame|frameset|ilayer|layer|bgsound|title|base)[^>]*>#i', "", $this->__cleaned);
749		} while ($oldstring != $this->__cleaned);
750		$this->__cleaned = str_replace(array("&amp;", "&lt;", "&gt;"), array("&amp;amp;", "&amp;lt;", "&amp;gt;"), $this->__cleaned);
751	}
752}
753?>