PageRenderTime 45ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/Cake/View/Helper.php

https://bitbucket.org/ttalov/fgcu_pci
PHP | 914 lines | 447 code | 77 blank | 390 comment | 109 complexity | 8a3cf07414ca3f86e5bf3181bc6c71b5 MD5 | raw file
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  4. * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
  5. *
  6. * Licensed under The MIT License
  7. * Redistributions of files must retain the above copyright notice.
  8. *
  9. * @copyright Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
  10. * @link http://cakephp.org CakePHP(tm) Project
  11. * @package Cake.View
  12. * @since CakePHP(tm) v 0.2.9
  13. * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
  14. */
  15. App::uses('Router', 'Routing');
  16. /**
  17. * Abstract base class for all other Helpers in CakePHP.
  18. * Provides common methods and features.
  19. *
  20. * @package Cake.View
  21. */
  22. class Helper extends Object {
  23. /**
  24. * List of helpers used by this helper
  25. *
  26. * @var array
  27. */
  28. public $helpers = array();
  29. /**
  30. * A helper lookup table used to lazy load helper objects.
  31. *
  32. * @var array
  33. */
  34. protected $_helperMap = array();
  35. /**
  36. * The current theme name if any.
  37. *
  38. * @var string
  39. */
  40. public $theme = null;
  41. /**
  42. * Request object
  43. *
  44. * @var CakeRequest
  45. */
  46. public $request = null;
  47. /**
  48. * Plugin path
  49. *
  50. * @var string
  51. */
  52. public $plugin = null;
  53. /**
  54. * Holds the fields array('field_name' => array('type' => 'string', 'length' => 100),
  55. * primaryKey and validates array('field_name')
  56. *
  57. * @var array
  58. */
  59. public $fieldset = array();
  60. /**
  61. * Holds tag templates.
  62. *
  63. * @var array
  64. */
  65. public $tags = array();
  66. /**
  67. * Holds the content to be cleaned.
  68. *
  69. * @var mixed
  70. */
  71. protected $_tainted = null;
  72. /**
  73. * Holds the cleaned content.
  74. *
  75. * @var mixed
  76. */
  77. protected $_cleaned = null;
  78. /**
  79. * The View instance this helper is attached to
  80. *
  81. * @var View
  82. */
  83. protected $_View;
  84. /**
  85. * A list of strings that should be treated as suffixes, or
  86. * sub inputs for a parent input. This is used for date/time
  87. * inputs primarily.
  88. *
  89. * @var array
  90. */
  91. protected $_fieldSuffixes = array(
  92. 'year', 'month', 'day', 'hour', 'min', 'second', 'meridian'
  93. );
  94. /**
  95. * The name of the current model entities are in scope of.
  96. *
  97. * @see Helper::setEntity()
  98. * @var string
  99. */
  100. protected $_modelScope;
  101. /**
  102. * The name of the current model association entities are in scope of.
  103. *
  104. * @see Helper::setEntity()
  105. * @var string
  106. */
  107. protected $_association;
  108. /**
  109. * The dot separated list of elements the current field entity is for.
  110. *
  111. * @see Helper::setEntity()
  112. * @var string
  113. */
  114. protected $_entityPath;
  115. /**
  116. * Minimized attributes
  117. *
  118. * @var array
  119. */
  120. protected $_minimizedAttributes = array(
  121. 'compact', 'checked', 'declare', 'readonly', 'disabled', 'selected',
  122. 'defer', 'ismap', 'nohref', 'noshade', 'nowrap', 'multiple', 'noresize',
  123. 'autoplay', 'controls', 'loop', 'muted'
  124. );
  125. /**
  126. * Format to attribute
  127. *
  128. * @var string
  129. */
  130. protected $_attributeFormat = '%s="%s"';
  131. /**
  132. * Format to attribute
  133. *
  134. * @var string
  135. */
  136. protected $_minimizedAttributeFormat = '%s="%s"';
  137. /**
  138. * Default Constructor
  139. *
  140. * @param View $View The View this helper is being attached to.
  141. * @param array $settings Configuration settings for the helper.
  142. */
  143. public function __construct(View $View, $settings = array()) {
  144. $this->_View = $View;
  145. $this->request = $View->request;
  146. if (!empty($this->helpers)) {
  147. $this->_helperMap = ObjectCollection::normalizeObjectArray($this->helpers);
  148. }
  149. }
  150. /**
  151. * Provide non fatal errors on missing method calls.
  152. *
  153. * @param string $method Method to invoke
  154. * @param array $params Array of params for the method.
  155. * @return void
  156. */
  157. public function __call($method, $params) {
  158. trigger_error(__d('cake_dev', 'Method %1$s::%2$s does not exist', get_class($this), $method), E_USER_WARNING);
  159. }
  160. /**
  161. * Lazy loads helpers. Provides access to deprecated request properties as well.
  162. *
  163. * @param string $name Name of the property being accessed.
  164. * @return mixed Helper or property found at $name
  165. */
  166. public function __get($name) {
  167. if (isset($this->_helperMap[$name]) && !isset($this->{$name})) {
  168. $settings = array_merge((array)$this->_helperMap[$name]['settings'], array('enabled' => false));
  169. $this->{$name} = $this->_View->loadHelper($this->_helperMap[$name]['class'], $settings);
  170. }
  171. if (isset($this->{$name})) {
  172. return $this->{$name};
  173. }
  174. switch ($name) {
  175. case 'base':
  176. case 'here':
  177. case 'webroot':
  178. case 'data':
  179. return $this->request->{$name};
  180. case 'action':
  181. return isset($this->request->params['action']) ? $this->request->params['action'] : '';
  182. case 'params':
  183. return $this->request;
  184. }
  185. }
  186. /**
  187. * Provides backwards compatibility access for setting values to the request object.
  188. *
  189. * @param string $name Name of the property being accessed.
  190. * @param mixed $value
  191. * @return mixed Return the $value
  192. */
  193. public function __set($name, $value) {
  194. switch ($name) {
  195. case 'base':
  196. case 'here':
  197. case 'webroot':
  198. case 'data':
  199. return $this->request->{$name} = $value;
  200. case 'action':
  201. return $this->request->params['action'] = $value;
  202. }
  203. return $this->{$name} = $value;
  204. }
  205. /**
  206. * Finds URL for specified action.
  207. *
  208. * Returns a URL pointing at the provided parameters.
  209. *
  210. * @param string|array $url Either a relative string url like `/products/view/23` or
  211. * an array of url parameters. Using an array for urls will allow you to leverage
  212. * the reverse routing features of CakePHP.
  213. * @param boolean $full If true, the full base URL will be prepended to the result
  214. * @return string Full translated URL with base path.
  215. * @link http://book.cakephp.org/2.0/en/views/helpers.html
  216. */
  217. public function url($url = null, $full = false) {
  218. return h(Router::url($url, $full));
  219. }
  220. /**
  221. * Checks if a file exists when theme is used, if no file is found default location is returned
  222. *
  223. * @param string $file The file to create a webroot path to.
  224. * @return string Web accessible path to file.
  225. */
  226. public function webroot($file) {
  227. $asset = explode('?', $file);
  228. $asset[1] = isset($asset[1]) ? '?' . $asset[1] : null;
  229. $webPath = "{$this->request->webroot}" . $asset[0];
  230. $file = $asset[0];
  231. if (!empty($this->theme)) {
  232. $file = trim($file, '/');
  233. $theme = $this->theme . '/';
  234. if (DS === '\\') {
  235. $file = str_replace('/', '\\', $file);
  236. }
  237. if (file_exists(Configure::read('App.www_root') . 'theme' . DS . $this->theme . DS . $file)) {
  238. $webPath = "{$this->request->webroot}theme/" . $theme . $asset[0];
  239. } else {
  240. $themePath = App::themePath($this->theme);
  241. $path = $themePath . 'webroot' . DS . $file;
  242. if (file_exists($path)) {
  243. $webPath = "{$this->request->webroot}theme/" . $theme . $asset[0];
  244. }
  245. }
  246. }
  247. if (strpos($webPath, '//') !== false) {
  248. return str_replace('//', '/', $webPath . $asset[1]);
  249. }
  250. return $webPath . $asset[1];
  251. }
  252. /**
  253. * Generate url for given asset file. Depending on options passed provides full url with domain name.
  254. * Also calls Helper::assetTimestamp() to add timestamp to local files
  255. *
  256. * @param string|array Path string or url array
  257. * @param array $options Options array. Possible keys:
  258. * `fullBase` Return full url with domain name
  259. * `pathPrefix` Path prefix for relative urls
  260. * `ext` Asset extension to append
  261. * `plugin` False value will prevent parsing path as a plugin
  262. * @return string Generated url
  263. */
  264. public function assetUrl($path, $options = array()) {
  265. if (is_array($path)) {
  266. $path = $this->url($path, !empty($options['fullBase']));
  267. } elseif (strpos($path, '://') === false) {
  268. if (!array_key_exists('plugin', $options) || $options['plugin'] !== false) {
  269. list($plugin, $path) = $this->_View->pluginSplit($path, false);
  270. }
  271. if (!empty($options['pathPrefix']) && $path[0] !== '/') {
  272. $path = $options['pathPrefix'] . $path;
  273. }
  274. if (
  275. !empty($options['ext']) &&
  276. strpos($path, '?') === false &&
  277. substr($path, -strlen($options['ext'])) !== $options['ext']
  278. ) {
  279. $path .= $options['ext'];
  280. }
  281. if (isset($plugin)) {
  282. $path = Inflector::underscore($plugin) . '/' . $path;
  283. }
  284. $path = h($this->assetTimestamp($this->webroot($path)));
  285. if (!empty($options['fullBase'])) {
  286. $base = $this->url('/', true);
  287. $len = strlen($this->request->webroot);
  288. if ($len) {
  289. $base = substr($base, 0, -$len);
  290. }
  291. $path = $base . $path;
  292. }
  293. }
  294. return $path;
  295. }
  296. /**
  297. * Adds a timestamp to a file based resource based on the value of `Asset.timestamp` in
  298. * Configure. If Asset.timestamp is true and debug > 0, or Asset.timestamp == 'force'
  299. * a timestamp will be added.
  300. *
  301. * @param string $path The file path to timestamp, the path must be inside WWW_ROOT
  302. * @return string Path with a timestamp added, or not.
  303. */
  304. public function assetTimestamp($path) {
  305. $stamp = Configure::read('Asset.timestamp');
  306. $timestampEnabled = $stamp === 'force' || ($stamp === true && Configure::read('debug') > 0);
  307. if ($timestampEnabled && strpos($path, '?') === false) {
  308. $filepath = preg_replace('/^' . preg_quote($this->request->webroot, '/') . '/', '', $path);
  309. $webrootPath = WWW_ROOT . str_replace('/', DS, $filepath);
  310. if (file_exists($webrootPath)) {
  311. return $path . '?' . @filemtime($webrootPath);
  312. }
  313. $segments = explode('/', ltrim($filepath, '/'));
  314. if ($segments[0] === 'theme') {
  315. $theme = $segments[1];
  316. unset($segments[0], $segments[1]);
  317. $themePath = App::themePath($theme) . 'webroot' . DS . implode(DS, $segments);
  318. return $path . '?' . @filemtime($themePath);
  319. } else {
  320. $plugin = Inflector::camelize($segments[0]);
  321. if (CakePlugin::loaded($plugin)) {
  322. unset($segments[0]);
  323. $pluginPath = CakePlugin::path($plugin) . 'webroot' . DS . implode(DS, $segments);
  324. return $path . '?' . @filemtime($pluginPath);
  325. }
  326. }
  327. }
  328. return $path;
  329. }
  330. /**
  331. * Used to remove harmful tags from content. Removes a number of well known XSS attacks
  332. * from content. However, is not guaranteed to remove all possibilities. Escaping
  333. * content is the best way to prevent all possible attacks.
  334. *
  335. * @param string|array $output Either an array of strings to clean or a single string to clean.
  336. * @return string|array cleaned content for output
  337. */
  338. public function clean($output) {
  339. $this->_reset();
  340. if (empty($output)) {
  341. return null;
  342. }
  343. if (is_array($output)) {
  344. foreach ($output as $key => $value) {
  345. $return[$key] = $this->clean($value);
  346. }
  347. return $return;
  348. }
  349. $this->_tainted = $output;
  350. $this->_clean();
  351. return $this->_cleaned;
  352. }
  353. /**
  354. * Returns a space-delimited string with items of the $options array. If a
  355. * key of $options array happens to be one of:
  356. *
  357. * - 'compact'
  358. * - 'checked'
  359. * - 'declare'
  360. * - 'readonly'
  361. * - 'disabled'
  362. * - 'selected'
  363. * - 'defer'
  364. * - 'ismap'
  365. * - 'nohref'
  366. * - 'noshade'
  367. * - 'nowrap'
  368. * - 'multiple'
  369. * - 'noresize'
  370. *
  371. * And its value is one of:
  372. *
  373. * - '1' (string)
  374. * - 1 (integer)
  375. * - true (boolean)
  376. * - 'true' (string)
  377. *
  378. * Then the value will be reset to be identical with key's name.
  379. * If the value is not one of these 3, the parameter is not output.
  380. *
  381. * 'escape' is a special option in that it controls the conversion of
  382. * attributes to their html-entity encoded equivalents. Set to false to disable html-encoding.
  383. *
  384. * If value for any option key is set to `null` or `false`, that option will be excluded from output.
  385. *
  386. * @param array $options Array of options.
  387. * @param array $exclude Array of options to be excluded, the options here will not be part of the return.
  388. * @param string $insertBefore String to be inserted before options.
  389. * @param string $insertAfter String to be inserted after options.
  390. * @return string Composed attributes.
  391. * @deprecated This method will be moved to HtmlHelper in 3.0
  392. */
  393. protected function _parseAttributes($options, $exclude = null, $insertBefore = ' ', $insertAfter = null) {
  394. if (!is_string($options)) {
  395. $options = (array)$options + array('escape' => true);
  396. if (!is_array($exclude)) {
  397. $exclude = array();
  398. }
  399. $exclude = array('escape' => true) + array_flip($exclude);
  400. $escape = $options['escape'];
  401. $attributes = array();
  402. foreach ($options as $key => $value) {
  403. if (!isset($exclude[$key]) && $value !== false && $value !== null) {
  404. $attributes[] = $this->_formatAttribute($key, $value, $escape);
  405. }
  406. }
  407. $out = implode(' ', $attributes);
  408. } else {
  409. $out = $options;
  410. }
  411. return $out ? $insertBefore . $out . $insertAfter : '';
  412. }
  413. /**
  414. * Formats an individual attribute, and returns the string value of the composed attribute.
  415. * Works with minimized attributes that have the same value as their name such as 'disabled' and 'checked'
  416. *
  417. * @param string $key The name of the attribute to create
  418. * @param string $value The value of the attribute to create.
  419. * @param boolean $escape Define if the value must be escaped
  420. * @return string The composed attribute.
  421. * @deprecated This method will be moved to HtmlHelper in 3.0
  422. */
  423. protected function _formatAttribute($key, $value, $escape = true) {
  424. $attribute = '';
  425. if (is_array($value)) {
  426. $value = implode(' ' , $value);
  427. }
  428. if (is_numeric($key)) {
  429. $attribute = sprintf($this->_minimizedAttributeFormat, $value, $value);
  430. } elseif (in_array($key, $this->_minimizedAttributes)) {
  431. if ($value === 1 || $value === true || $value === 'true' || $value === '1' || $value == $key) {
  432. $attribute = sprintf($this->_minimizedAttributeFormat, $key, $key);
  433. }
  434. } else {
  435. $attribute = sprintf($this->_attributeFormat, $key, ($escape ? h($value) : $value));
  436. }
  437. return $attribute;
  438. }
  439. /**
  440. * Sets this helper's model and field properties to the dot-separated value-pair in $entity.
  441. *
  442. * @param string $entity A field name, like "ModelName.fieldName" or "ModelName.ID.fieldName"
  443. * @param boolean $setScope Sets the view scope to the model specified in $tagValue
  444. * @return void
  445. */
  446. public function setEntity($entity, $setScope = false) {
  447. if ($entity === null) {
  448. $this->_modelScope = false;
  449. }
  450. if ($setScope === true) {
  451. $this->_modelScope = $entity;
  452. }
  453. $parts = array_values(Hash::filter(explode('.', $entity)));
  454. if (empty($parts)) {
  455. return;
  456. }
  457. $count = count($parts);
  458. $lastPart = isset($parts[$count - 1]) ? $parts[$count - 1] : null;
  459. // Either 'body' or 'date.month' type inputs.
  460. if (
  461. ($count === 1 && $this->_modelScope && $setScope == false) ||
  462. (
  463. $count === 2 &&
  464. in_array($lastPart, $this->_fieldSuffixes) &&
  465. $this->_modelScope &&
  466. $parts[0] !== $this->_modelScope
  467. )
  468. ) {
  469. $entity = $this->_modelScope . '.' . $entity;
  470. }
  471. // 0.name, 0.created.month style inputs. Excludes inputs with the modelScope in them.
  472. if (
  473. $count >= 2 &&
  474. is_numeric($parts[0]) &&
  475. !is_numeric($parts[1]) &&
  476. $this->_modelScope &&
  477. strpos($entity, $this->_modelScope) === false
  478. ) {
  479. $entity = $this->_modelScope . '.' . $entity;
  480. }
  481. $this->_association = null;
  482. $isHabtm = (
  483. isset($this->fieldset[$this->_modelScope]['fields'][$parts[0]]['type']) &&
  484. $this->fieldset[$this->_modelScope]['fields'][$parts[0]]['type'] === 'multiple' &&
  485. $count == 1
  486. );
  487. // habtm models are special
  488. if ($count == 1 && $isHabtm) {
  489. $this->_association = $parts[0];
  490. $entity = $parts[0] . '.' . $parts[0];
  491. } else {
  492. // check for associated model.
  493. $reversed = array_reverse($parts);
  494. foreach ($reversed as $i => $part) {
  495. if ($i > 0 && preg_match('/^[A-Z]/', $part)) {
  496. $this->_association = $part;
  497. break;
  498. }
  499. }
  500. }
  501. $this->_entityPath = $entity;
  502. }
  503. /**
  504. * Returns the entity reference of the current context as an array of identity parts
  505. *
  506. * @return array An array containing the identity elements of an entity
  507. */
  508. public function entity() {
  509. return explode('.', $this->_entityPath);
  510. }
  511. /**
  512. * Gets the currently-used model of the rendering context.
  513. *
  514. * @return string
  515. */
  516. public function model() {
  517. if ($this->_association) {
  518. return $this->_association;
  519. }
  520. return $this->_modelScope;
  521. }
  522. /**
  523. * Gets the currently-used model field of the rendering context.
  524. * Strips off field suffixes such as year, month, day, hour, min, meridian
  525. * when the current entity is longer than 2 elements.
  526. *
  527. * @return string
  528. */
  529. public function field() {
  530. $entity = $this->entity();
  531. $count = count($entity);
  532. $last = $entity[$count - 1];
  533. if ($count > 2 && in_array($last, $this->_fieldSuffixes)) {
  534. $last = isset($entity[$count - 2]) ? $entity[$count - 2] : null;
  535. }
  536. return $last;
  537. }
  538. /**
  539. * Generates a DOM ID for the selected element, if one is not set.
  540. * Uses the current View::entity() settings to generate a CamelCased id attribute.
  541. *
  542. * @param array|string $options Either an array of html attributes to add $id into, or a string
  543. * with a view entity path to get a domId for.
  544. * @param string $id The name of the 'id' attribute.
  545. * @return mixed If $options was an array, an array will be returned with $id set. If a string
  546. * was supplied, a string will be returned.
  547. * @todo Refactor this method to not have as many input/output options.
  548. */
  549. public function domId($options = null, $id = 'id') {
  550. if (is_array($options) && array_key_exists($id, $options) && $options[$id] === null) {
  551. unset($options[$id]);
  552. return $options;
  553. } elseif (!is_array($options) && $options !== null) {
  554. $this->setEntity($options);
  555. return $this->domId();
  556. }
  557. $entity = $this->entity();
  558. $model = array_shift($entity);
  559. $dom = $model . join('', array_map(array('Inflector', 'camelize'), $entity));
  560. if (is_array($options) && !array_key_exists($id, $options)) {
  561. $options[$id] = $dom;
  562. } elseif ($options === null) {
  563. return $dom;
  564. }
  565. return $options;
  566. }
  567. /**
  568. * Gets the input field name for the current tag. Creates input name attributes
  569. * using CakePHP's data[Model][field] formatting.
  570. *
  571. * @param array|string $options If an array, should be an array of attributes that $key needs to be added to.
  572. * If a string or null, will be used as the View entity.
  573. * @param string $field
  574. * @param string $key The name of the attribute to be set, defaults to 'name'
  575. * @return mixed If an array was given for $options, an array with $key set will be returned.
  576. * If a string was supplied a string will be returned.
  577. * @todo Refactor this method to not have as many input/output options.
  578. */
  579. protected function _name($options = array(), $field = null, $key = 'name') {
  580. if ($options === null) {
  581. $options = array();
  582. } elseif (is_string($options)) {
  583. $field = $options;
  584. $options = 0;
  585. }
  586. if (!empty($field)) {
  587. $this->setEntity($field);
  588. }
  589. if (is_array($options) && array_key_exists($key, $options)) {
  590. return $options;
  591. }
  592. switch ($field) {
  593. case '_method':
  594. $name = $field;
  595. break;
  596. default:
  597. $name = 'data[' . implode('][', $this->entity()) . ']';
  598. break;
  599. }
  600. if (is_array($options)) {
  601. $options[$key] = $name;
  602. return $options;
  603. } else {
  604. return $name;
  605. }
  606. }
  607. /**
  608. * Gets the data for the current tag
  609. *
  610. * @param array|string $options If an array, should be an array of attributes that $key needs to be added to.
  611. * If a string or null, will be used as the View entity.
  612. * @param string $field
  613. * @param string $key The name of the attribute to be set, defaults to 'value'
  614. * @return mixed If an array was given for $options, an array with $key set will be returned.
  615. * If a string was supplied a string will be returned.
  616. * @todo Refactor this method to not have as many input/output options.
  617. */
  618. public function value($options = array(), $field = null, $key = 'value') {
  619. if ($options === null) {
  620. $options = array();
  621. } elseif (is_string($options)) {
  622. $field = $options;
  623. $options = 0;
  624. }
  625. if (is_array($options) && isset($options[$key])) {
  626. return $options;
  627. }
  628. if (!empty($field)) {
  629. $this->setEntity($field);
  630. }
  631. $result = null;
  632. $data = $this->request->data;
  633. $entity = $this->entity();
  634. if (!empty($data) && is_array($data) && !empty($entity)) {
  635. $result = Hash::get($data, implode('.', $entity));
  636. }
  637. $habtmKey = $this->field();
  638. if (empty($result) && isset($data[$habtmKey][$habtmKey]) && is_array($data[$habtmKey])) {
  639. $result = $data[$habtmKey][$habtmKey];
  640. } elseif (empty($result) && isset($data[$habtmKey]) && is_array($data[$habtmKey])) {
  641. if (ClassRegistry::isKeySet($habtmKey)) {
  642. $model = ClassRegistry::getObject($habtmKey);
  643. $result = $this->_selectedArray($data[$habtmKey], $model->primaryKey);
  644. }
  645. }
  646. if (is_array($options)) {
  647. if ($result === null && isset($options['default'])) {
  648. $result = $options['default'];
  649. }
  650. unset($options['default']);
  651. }
  652. if (is_array($options)) {
  653. $options[$key] = $result;
  654. return $options;
  655. } else {
  656. return $result;
  657. }
  658. }
  659. /**
  660. * Sets the defaults for an input tag. Will set the
  661. * name, value, and id attributes for an array of html attributes. Will also
  662. * add a 'form-error' class if the field contains validation errors.
  663. *
  664. * @param string $field The field name to initialize.
  665. * @param array $options Array of options to use while initializing an input field.
  666. * @return array Array options for the form input.
  667. */
  668. protected function _initInputField($field, $options = array()) {
  669. if ($field !== null) {
  670. $this->setEntity($field);
  671. }
  672. $options = (array)$options;
  673. $options = $this->_name($options);
  674. $options = $this->value($options);
  675. $options = $this->domId($options);
  676. return $options;
  677. }
  678. /**
  679. * Adds the given class to the element options
  680. *
  681. * @param array $options Array options/attributes to add a class to
  682. * @param string $class The classname being added.
  683. * @param string $key the key to use for class.
  684. * @return array Array of options with $key set.
  685. */
  686. public function addClass($options = array(), $class = null, $key = 'class') {
  687. if (isset($options[$key]) && trim($options[$key]) != '') {
  688. $options[$key] .= ' ' . $class;
  689. } else {
  690. $options[$key] = $class;
  691. }
  692. return $options;
  693. }
  694. /**
  695. * Returns a string generated by a helper method
  696. *
  697. * This method can be overridden in subclasses to do generalized output post-processing
  698. *
  699. * @param string $str String to be output.
  700. * @return string
  701. * @deprecated This method will be removed in future versions.
  702. */
  703. public function output($str) {
  704. return $str;
  705. }
  706. /**
  707. * Before render callback. beforeRender is called before the view file is rendered.
  708. *
  709. * Overridden in subclasses.
  710. *
  711. * @param string $viewFile The view file that is going to be rendered
  712. * @return void
  713. */
  714. public function beforeRender($viewFile) {
  715. }
  716. /**
  717. * After render callback. afterRender is called after the view file is rendered
  718. * but before the layout has been rendered.
  719. *
  720. * Overridden in subclasses.
  721. *
  722. * @param string $viewFile The view file that was rendered.
  723. * @return void
  724. */
  725. public function afterRender($viewFile) {
  726. }
  727. /**
  728. * Before layout callback. beforeLayout is called before the layout is rendered.
  729. *
  730. * Overridden in subclasses.
  731. *
  732. * @param string $layoutFile The layout about to be rendered.
  733. * @return void
  734. */
  735. public function beforeLayout($layoutFile) {
  736. }
  737. /**
  738. * After layout callback. afterLayout is called after the layout has rendered.
  739. *
  740. * Overridden in subclasses.
  741. *
  742. * @param string $layoutFile The layout file that was rendered.
  743. * @return void
  744. */
  745. public function afterLayout($layoutFile) {
  746. }
  747. /**
  748. * Before render file callback.
  749. * Called before any view fragment is rendered.
  750. *
  751. * Overridden in subclasses.
  752. *
  753. * @param string $viewFile The file about to be rendered.
  754. * @return void
  755. */
  756. public function beforeRenderFile($viewfile) {
  757. }
  758. /**
  759. * After render file callback.
  760. * Called after any view fragment is rendered.
  761. *
  762. * Overridden in subclasses.
  763. *
  764. * @param string $viewFile The file just be rendered.
  765. * @param string $content The content that was rendered.
  766. * @return void
  767. */
  768. public function afterRenderFile($viewfile, $content) {
  769. }
  770. /**
  771. * Transforms a recordset from a hasAndBelongsToMany association to a list of selected
  772. * options for a multiple select element
  773. *
  774. * @param string|array $data
  775. * @param string $key
  776. * @return array
  777. */
  778. protected function _selectedArray($data, $key = 'id') {
  779. if (!is_array($data)) {
  780. $model = $data;
  781. if (!empty($this->request->data[$model][$model])) {
  782. return $this->request->data[$model][$model];
  783. }
  784. if (!empty($this->request->data[$model])) {
  785. $data = $this->request->data[$model];
  786. }
  787. }
  788. $array = array();
  789. if (!empty($data)) {
  790. foreach ($data as $row) {
  791. if (isset($row[$key])) {
  792. $array[$row[$key]] = $row[$key];
  793. }
  794. }
  795. }
  796. return empty($array) ? null : $array;
  797. }
  798. /**
  799. * Resets the vars used by Helper::clean() to null
  800. *
  801. * @return void
  802. */
  803. protected function _reset() {
  804. $this->_tainted = null;
  805. $this->_cleaned = null;
  806. }
  807. /**
  808. * Removes harmful content from output
  809. *
  810. * @return void
  811. */
  812. protected function _clean() {
  813. if (get_magic_quotes_gpc()) {
  814. $this->_cleaned = stripslashes($this->_tainted);
  815. } else {
  816. $this->_cleaned = $this->_tainted;
  817. }
  818. $this->_cleaned = str_replace(array("&amp;", "&lt;", "&gt;"), array("&amp;amp;", "&amp;lt;", "&amp;gt;"), $this->_cleaned);
  819. $this->_cleaned = preg_replace('#(&\#*\w+)[\x00-\x20]+;#u', "$1;", $this->_cleaned);
  820. $this->_cleaned = preg_replace('#(&\#x*)([0-9A-F]+);*#iu', "$1$2;", $this->_cleaned);
  821. $this->_cleaned = html_entity_decode($this->_cleaned, ENT_COMPAT, "UTF-8");
  822. $this->_cleaned = preg_replace('#(<[^>]+[\x00-\x20\"\'\/])(on|xmlns)[^>]*>#iUu', "$1>", $this->_cleaned);
  823. $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);
  824. $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);
  825. $this->_cleaned = preg_replace('#([a-z]*)[\x00-\x20]*=*([\'\"]*)[\x00-\x20]*-moz-binding[\x00-\x20]*:#iUu', '$1=$2nomozbinding...', $this->_cleaned);
  826. $this->_cleaned = preg_replace('#([a-z]*)[\x00-\x20]*=([\'\"]*)[\x00-\x20]*data[\x00-\x20]*:#Uu', '$1=$2nodata...', $this->_cleaned);
  827. $this->_cleaned = preg_replace('#(<[^>]+)style[\x00-\x20]*=[\x00-\x20]*([\`\'\"]*).*expression[\x00-\x20]*\([^>]*>#iU', "$1>", $this->_cleaned);
  828. $this->_cleaned = preg_replace('#(<[^>]+)style[\x00-\x20]*=[\x00-\x20]*([\`\'\"]*).*behaviour[\x00-\x20]*\([^>]*>#iU', "$1>", $this->_cleaned);
  829. $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);
  830. $this->_cleaned = preg_replace('#</*\w+:\w[^>]*>#i', "", $this->_cleaned);
  831. do {
  832. $oldstring = $this->_cleaned;
  833. $this->_cleaned = preg_replace('#</*(applet|meta|xml|blink|link|style|script|embed|object|iframe|frame|frameset|ilayer|layer|bgsound|title|base)[^>]*>#i', "", $this->_cleaned);
  834. } while ($oldstring != $this->_cleaned);
  835. $this->_cleaned = str_replace(array("&amp;", "&lt;", "&gt;"), array("&amp;amp;", "&amp;lt;", "&amp;gt;"), $this->_cleaned);
  836. }
  837. }