PageRenderTime 34ms CodeModel.GetById 7ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/Cake/View/Helper.php

https://bitbucket.org/npitts/easyuat2.0
PHP | 909 lines | 442 code | 77 blank | 390 comment | 107 complexity | baf3092dd631f5282e883dc43b51f92f 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 mixed $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. $path = $this->url('/', true) . $path;
  287. }
  288. }
  289. return $path;
  290. }
  291. /**
  292. * Adds a timestamp to a file based resource based on the value of `Asset.timestamp` in
  293. * Configure. If Asset.timestamp is true and debug > 0, or Asset.timestamp == 'force'
  294. * a timestamp will be added.
  295. *
  296. * @param string $path The file path to timestamp, the path must be inside WWW_ROOT
  297. * @return string Path with a timestamp added, or not.
  298. */
  299. public function assetTimestamp($path) {
  300. $stamp = Configure::read('Asset.timestamp');
  301. $timestampEnabled = $stamp === 'force' || ($stamp === true && Configure::read('debug') > 0);
  302. if ($timestampEnabled && strpos($path, '?') === false) {
  303. $filepath = preg_replace('/^' . preg_quote($this->request->webroot, '/') . '/', '', $path);
  304. $webrootPath = WWW_ROOT . str_replace('/', DS, $filepath);
  305. if (file_exists($webrootPath)) {
  306. return $path . '?' . @filemtime($webrootPath);
  307. }
  308. $segments = explode('/', ltrim($filepath, '/'));
  309. if ($segments[0] === 'theme') {
  310. $theme = $segments[1];
  311. unset($segments[0], $segments[1]);
  312. $themePath = App::themePath($theme) . 'webroot' . DS . implode(DS, $segments);
  313. return $path . '?' . @filemtime($themePath);
  314. } else {
  315. $plugin = Inflector::camelize($segments[0]);
  316. if (CakePlugin::loaded($plugin)) {
  317. unset($segments[0]);
  318. $pluginPath = CakePlugin::path($plugin) . 'webroot' . DS . implode(DS, $segments);
  319. return $path . '?' . @filemtime($pluginPath);
  320. }
  321. }
  322. }
  323. return $path;
  324. }
  325. /**
  326. * Used to remove harmful tags from content. Removes a number of well known XSS attacks
  327. * from content. However, is not guaranteed to remove all possibilities. Escaping
  328. * content is the best way to prevent all possible attacks.
  329. *
  330. * @param mixed $output Either an array of strings to clean or a single string to clean.
  331. * @return string|array cleaned content for output
  332. */
  333. public function clean($output) {
  334. $this->_reset();
  335. if (empty($output)) {
  336. return null;
  337. }
  338. if (is_array($output)) {
  339. foreach ($output as $key => $value) {
  340. $return[$key] = $this->clean($value);
  341. }
  342. return $return;
  343. }
  344. $this->_tainted = $output;
  345. $this->_clean();
  346. return $this->_cleaned;
  347. }
  348. /**
  349. * Returns a space-delimited string with items of the $options array. If a
  350. * key of $options array happens to be one of:
  351. *
  352. * - 'compact'
  353. * - 'checked'
  354. * - 'declare'
  355. * - 'readonly'
  356. * - 'disabled'
  357. * - 'selected'
  358. * - 'defer'
  359. * - 'ismap'
  360. * - 'nohref'
  361. * - 'noshade'
  362. * - 'nowrap'
  363. * - 'multiple'
  364. * - 'noresize'
  365. *
  366. * And its value is one of:
  367. *
  368. * - '1' (string)
  369. * - 1 (integer)
  370. * - true (boolean)
  371. * - 'true' (string)
  372. *
  373. * Then the value will be reset to be identical with key's name.
  374. * If the value is not one of these 3, the parameter is not output.
  375. *
  376. * 'escape' is a special option in that it controls the conversion of
  377. * attributes to their html-entity encoded equivalents. Set to false to disable html-encoding.
  378. *
  379. * If value for any option key is set to `null` or `false`, that option will be excluded from output.
  380. *
  381. * @param array $options Array of options.
  382. * @param array $exclude Array of options to be excluded, the options here will not be part of the return.
  383. * @param string $insertBefore String to be inserted before options.
  384. * @param string $insertAfter String to be inserted after options.
  385. * @return string Composed attributes.
  386. * @deprecated This method will be moved to HtmlHelper in 3.0
  387. */
  388. protected function _parseAttributes($options, $exclude = null, $insertBefore = ' ', $insertAfter = null) {
  389. if (!is_string($options)) {
  390. $options = (array)$options + array('escape' => true);
  391. if (!is_array($exclude)) {
  392. $exclude = array();
  393. }
  394. $exclude = array('escape' => true) + array_flip($exclude);
  395. $escape = $options['escape'];
  396. $attributes = array();
  397. foreach ($options as $key => $value) {
  398. if (!isset($exclude[$key]) && $value !== false && $value !== null) {
  399. $attributes[] = $this->_formatAttribute($key, $value, $escape);
  400. }
  401. }
  402. $out = implode(' ', $attributes);
  403. } else {
  404. $out = $options;
  405. }
  406. return $out ? $insertBefore . $out . $insertAfter : '';
  407. }
  408. /**
  409. * Formats an individual attribute, and returns the string value of the composed attribute.
  410. * Works with minimized attributes that have the same value as their name such as 'disabled' and 'checked'
  411. *
  412. * @param string $key The name of the attribute to create
  413. * @param string $value The value of the attribute to create.
  414. * @param boolean $escape Define if the value must be escaped
  415. * @return string The composed attribute.
  416. * @deprecated This method will be moved to HtmlHelper in 3.0
  417. */
  418. protected function _formatAttribute($key, $value, $escape = true) {
  419. $attribute = '';
  420. if (is_array($value)) {
  421. $value = implode(' ' , $value);
  422. }
  423. if (is_numeric($key)) {
  424. $attribute = sprintf($this->_minimizedAttributeFormat, $value, $value);
  425. } elseif (in_array($key, $this->_minimizedAttributes)) {
  426. if ($value === 1 || $value === true || $value === 'true' || $value === '1' || $value == $key) {
  427. $attribute = sprintf($this->_minimizedAttributeFormat, $key, $key);
  428. }
  429. } else {
  430. $attribute = sprintf($this->_attributeFormat, $key, ($escape ? h($value) : $value));
  431. }
  432. return $attribute;
  433. }
  434. /**
  435. * Sets this helper's model and field properties to the dot-separated value-pair in $entity.
  436. *
  437. * @param mixed $entity A field name, like "ModelName.fieldName" or "ModelName.ID.fieldName"
  438. * @param boolean $setScope Sets the view scope to the model specified in $tagValue
  439. * @return void
  440. */
  441. public function setEntity($entity, $setScope = false) {
  442. if ($entity === null) {
  443. $this->_modelScope = false;
  444. }
  445. if ($setScope === true) {
  446. $this->_modelScope = $entity;
  447. }
  448. $parts = array_values(Set::filter(explode('.', $entity), true));
  449. if (empty($parts)) {
  450. return;
  451. }
  452. $count = count($parts);
  453. $lastPart = isset($parts[$count - 1]) ? $parts[$count - 1] : null;
  454. // Either 'body' or 'date.month' type inputs.
  455. if (
  456. ($count === 1 && $this->_modelScope && $setScope == false) ||
  457. (
  458. $count === 2 &&
  459. in_array($lastPart, $this->_fieldSuffixes) &&
  460. $this->_modelScope &&
  461. $parts[0] !== $this->_modelScope
  462. )
  463. ) {
  464. $entity = $this->_modelScope . '.' . $entity;
  465. }
  466. // 0.name, 0.created.month style inputs. Excludes inputs with the modelScope in them.
  467. if (
  468. $count >= 2 &&
  469. is_numeric($parts[0]) &&
  470. !is_numeric($parts[1]) &&
  471. $this->_modelScope &&
  472. strpos($entity, $this->_modelScope) === false
  473. ) {
  474. $entity = $this->_modelScope . '.' . $entity;
  475. }
  476. $this->_association = null;
  477. $isHabtm = (
  478. isset($this->fieldset[$this->_modelScope]['fields'][$parts[0]]['type']) &&
  479. $this->fieldset[$this->_modelScope]['fields'][$parts[0]]['type'] === 'multiple' &&
  480. $count == 1
  481. );
  482. // habtm models are special
  483. if ($count == 1 && $isHabtm) {
  484. $this->_association = $parts[0];
  485. $entity = $parts[0] . '.' . $parts[0];
  486. } else {
  487. // check for associated model.
  488. $reversed = array_reverse($parts);
  489. foreach ($reversed as $i => $part) {
  490. if ($i > 0 && preg_match('/^[A-Z]/', $part)) {
  491. $this->_association = $part;
  492. break;
  493. }
  494. }
  495. }
  496. $this->_entityPath = $entity;
  497. }
  498. /**
  499. * Returns the entity reference of the current context as an array of identity parts
  500. *
  501. * @return array An array containing the identity elements of an entity
  502. */
  503. public function entity() {
  504. return explode('.', $this->_entityPath);
  505. }
  506. /**
  507. * Gets the currently-used model of the rendering context.
  508. *
  509. * @return string
  510. */
  511. public function model() {
  512. if ($this->_association) {
  513. return $this->_association;
  514. }
  515. return $this->_modelScope;
  516. }
  517. /**
  518. * Gets the currently-used model field of the rendering context.
  519. * Strips off field suffixes such as year, month, day, hour, min, meridian
  520. * when the current entity is longer than 2 elements.
  521. *
  522. * @return string
  523. */
  524. public function field() {
  525. $entity = $this->entity();
  526. $count = count($entity);
  527. $last = $entity[$count - 1];
  528. if ($count > 2 && in_array($last, $this->_fieldSuffixes)) {
  529. $last = isset($entity[$count - 2]) ? $entity[$count - 2] : null;
  530. }
  531. return $last;
  532. }
  533. /**
  534. * Generates a DOM ID for the selected element, if one is not set.
  535. * Uses the current View::entity() settings to generate a CamelCased id attribute.
  536. *
  537. * @param mixed $options Either an array of html attributes to add $id into, or a string
  538. * with a view entity path to get a domId for.
  539. * @param string $id The name of the 'id' attribute.
  540. * @return mixed If $options was an array, an array will be returned with $id set. If a string
  541. * was supplied, a string will be returned.
  542. * @todo Refactor this method to not have as many input/output options.
  543. */
  544. public function domId($options = null, $id = 'id') {
  545. if (is_array($options) && array_key_exists($id, $options) && $options[$id] === null) {
  546. unset($options[$id]);
  547. return $options;
  548. } elseif (!is_array($options) && $options !== null) {
  549. $this->setEntity($options);
  550. return $this->domId();
  551. }
  552. $entity = $this->entity();
  553. $model = array_shift($entity);
  554. $dom = $model . join('', array_map(array('Inflector', 'camelize'), $entity));
  555. if (is_array($options) && !array_key_exists($id, $options)) {
  556. $options[$id] = $dom;
  557. } elseif ($options === null) {
  558. return $dom;
  559. }
  560. return $options;
  561. }
  562. /**
  563. * Gets the input field name for the current tag. Creates input name attributes
  564. * using CakePHP's data[Model][field] formatting.
  565. *
  566. * @param mixed $options If an array, should be an array of attributes that $key needs to be added to.
  567. * If a string or null, will be used as the View entity.
  568. * @param string $field
  569. * @param string $key The name of the attribute to be set, defaults to 'name'
  570. * @return mixed If an array was given for $options, an array with $key set will be returned.
  571. * If a string was supplied a string will be returned.
  572. * @todo Refactor this method to not have as many input/output options.
  573. */
  574. protected function _name($options = array(), $field = null, $key = 'name') {
  575. if ($options === null) {
  576. $options = array();
  577. } elseif (is_string($options)) {
  578. $field = $options;
  579. $options = 0;
  580. }
  581. if (!empty($field)) {
  582. $this->setEntity($field);
  583. }
  584. if (is_array($options) && array_key_exists($key, $options)) {
  585. return $options;
  586. }
  587. switch ($field) {
  588. case '_method':
  589. $name = $field;
  590. break;
  591. default:
  592. $name = 'data[' . implode('][', $this->entity()) . ']';
  593. break;
  594. }
  595. if (is_array($options)) {
  596. $options[$key] = $name;
  597. return $options;
  598. } else {
  599. return $name;
  600. }
  601. }
  602. /**
  603. * Gets the data for the current tag
  604. *
  605. * @param mixed $options If an array, should be an array of attributes that $key needs to be added to.
  606. * If a string or null, will be used as the View entity.
  607. * @param string $field
  608. * @param string $key The name of the attribute to be set, defaults to 'value'
  609. * @return mixed If an array was given for $options, an array with $key set will be returned.
  610. * If a string was supplied a string will be returned.
  611. * @todo Refactor this method to not have as many input/output options.
  612. */
  613. public function value($options = array(), $field = null, $key = 'value') {
  614. if ($options === null) {
  615. $options = array();
  616. } elseif (is_string($options)) {
  617. $field = $options;
  618. $options = 0;
  619. }
  620. if (is_array($options) && isset($options[$key])) {
  621. return $options;
  622. }
  623. if (!empty($field)) {
  624. $this->setEntity($field);
  625. }
  626. $result = null;
  627. $data = $this->request->data;
  628. $entity = $this->entity();
  629. if (!empty($data) && !empty($entity)) {
  630. $result = Set::extract(implode('.', $entity), $data);
  631. }
  632. $habtmKey = $this->field();
  633. if (empty($result) && isset($data[$habtmKey][$habtmKey]) && is_array($data[$habtmKey])) {
  634. $result = $data[$habtmKey][$habtmKey];
  635. } elseif (empty($result) && isset($data[$habtmKey]) && is_array($data[$habtmKey])) {
  636. if (ClassRegistry::isKeySet($habtmKey)) {
  637. $model = ClassRegistry::getObject($habtmKey);
  638. $result = $this->_selectedArray($data[$habtmKey], $model->primaryKey);
  639. }
  640. }
  641. if (is_array($options)) {
  642. if ($result === null && isset($options['default'])) {
  643. $result = $options['default'];
  644. }
  645. unset($options['default']);
  646. }
  647. if (is_array($options)) {
  648. $options[$key] = $result;
  649. return $options;
  650. } else {
  651. return $result;
  652. }
  653. }
  654. /**
  655. * Sets the defaults for an input tag. Will set the
  656. * name, value, and id attributes for an array of html attributes. Will also
  657. * add a 'form-error' class if the field contains validation errors.
  658. *
  659. * @param string $field The field name to initialize.
  660. * @param array $options Array of options to use while initializing an input field.
  661. * @return array Array options for the form input.
  662. */
  663. protected function _initInputField($field, $options = array()) {
  664. if ($field !== null) {
  665. $this->setEntity($field);
  666. }
  667. $options = (array)$options;
  668. $options = $this->_name($options);
  669. $options = $this->value($options);
  670. $options = $this->domId($options);
  671. return $options;
  672. }
  673. /**
  674. * Adds the given class to the element options
  675. *
  676. * @param array $options Array options/attributes to add a class to
  677. * @param string $class The classname being added.
  678. * @param string $key the key to use for class.
  679. * @return array Array of options with $key set.
  680. */
  681. public function addClass($options = array(), $class = null, $key = 'class') {
  682. if (isset($options[$key]) && trim($options[$key]) != '') {
  683. $options[$key] .= ' ' . $class;
  684. } else {
  685. $options[$key] = $class;
  686. }
  687. return $options;
  688. }
  689. /**
  690. * Returns a string generated by a helper method
  691. *
  692. * This method can be overridden in subclasses to do generalized output post-processing
  693. *
  694. * @param string $str String to be output.
  695. * @return string
  696. * @deprecated This method will be removed in future versions.
  697. */
  698. public function output($str) {
  699. return $str;
  700. }
  701. /**
  702. * Before render callback. beforeRender is called before the view file is rendered.
  703. *
  704. * Overridden in subclasses.
  705. *
  706. * @param string $viewFile The view file that is going to be rendered
  707. * @return void
  708. */
  709. public function beforeRender($viewFile) {
  710. }
  711. /**
  712. * After render callback. afterRender is called after the view file is rendered
  713. * but before the layout has been rendered.
  714. *
  715. * Overridden in subclasses.
  716. *
  717. * @param string $viewFile The view file that was rendered.
  718. * @return void
  719. */
  720. public function afterRender($viewFile) {
  721. }
  722. /**
  723. * Before layout callback. beforeLayout is called before the layout is rendered.
  724. *
  725. * Overridden in subclasses.
  726. *
  727. * @param string $layoutFile The layout about to be rendered.
  728. * @return void
  729. */
  730. public function beforeLayout($layoutFile) {
  731. }
  732. /**
  733. * After layout callback. afterLayout is called after the layout has rendered.
  734. *
  735. * Overridden in subclasses.
  736. *
  737. * @param string $layoutFile The layout file that was rendered.
  738. * @return void
  739. */
  740. public function afterLayout($layoutFile) {
  741. }
  742. /**
  743. * Before render file callback.
  744. * Called before any view fragment is rendered.
  745. *
  746. * Overridden in subclasses.
  747. *
  748. * @param string $viewFile The file about to be rendered.
  749. * @return void
  750. */
  751. public function beforeRenderFile($viewfile) {
  752. }
  753. /**
  754. * After render file callback.
  755. * Called after any view fragment is rendered.
  756. *
  757. * Overridden in subclasses.
  758. *
  759. * @param string $viewFile The file just be rendered.
  760. * @param string $content The content that was rendered.
  761. * @return void
  762. */
  763. public function afterRenderFile($viewfile, $content) {
  764. }
  765. /**
  766. * Transforms a recordset from a hasAndBelongsToMany association to a list of selected
  767. * options for a multiple select element
  768. *
  769. * @param mixed $data
  770. * @param string $key
  771. * @return array
  772. */
  773. protected function _selectedArray($data, $key = 'id') {
  774. if (!is_array($data)) {
  775. $model = $data;
  776. if (!empty($this->request->data[$model][$model])) {
  777. return $this->request->data[$model][$model];
  778. }
  779. if (!empty($this->request->data[$model])) {
  780. $data = $this->request->data[$model];
  781. }
  782. }
  783. $array = array();
  784. if (!empty($data)) {
  785. foreach ($data as $row) {
  786. if (isset($row[$key])) {
  787. $array[$row[$key]] = $row[$key];
  788. }
  789. }
  790. }
  791. return empty($array) ? null : $array;
  792. }
  793. /**
  794. * Resets the vars used by Helper::clean() to null
  795. *
  796. * @return void
  797. */
  798. protected function _reset() {
  799. $this->_tainted = null;
  800. $this->_cleaned = null;
  801. }
  802. /**
  803. * Removes harmful content from output
  804. *
  805. * @return void
  806. */
  807. protected function _clean() {
  808. if (get_magic_quotes_gpc()) {
  809. $this->_cleaned = stripslashes($this->_tainted);
  810. } else {
  811. $this->_cleaned = $this->_tainted;
  812. }
  813. $this->_cleaned = str_replace(array("&amp;", "&lt;", "&gt;"), array("&amp;amp;", "&amp;lt;", "&amp;gt;"), $this->_cleaned);
  814. $this->_cleaned = preg_replace('#(&\#*\w+)[\x00-\x20]+;#u', "$1;", $this->_cleaned);
  815. $this->_cleaned = preg_replace('#(&\#x*)([0-9A-F]+);*#iu', "$1$2;", $this->_cleaned);
  816. $this->_cleaned = html_entity_decode($this->_cleaned, ENT_COMPAT, "UTF-8");
  817. $this->_cleaned = preg_replace('#(<[^>]+[\x00-\x20\"\'\/])(on|xmlns)[^>]*>#iUu', "$1>", $this->_cleaned);
  818. $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);
  819. $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);
  820. $this->_cleaned = preg_replace('#([a-z]*)[\x00-\x20]*=*([\'\"]*)[\x00-\x20]*-moz-binding[\x00-\x20]*:#iUu', '$1=$2nomozbinding...', $this->_cleaned);
  821. $this->_cleaned = preg_replace('#([a-z]*)[\x00-\x20]*=([\'\"]*)[\x00-\x20]*data[\x00-\x20]*:#Uu', '$1=$2nodata...', $this->_cleaned);
  822. $this->_cleaned = preg_replace('#(<[^>]+)style[\x00-\x20]*=[\x00-\x20]*([\`\'\"]*).*expression[\x00-\x20]*\([^>]*>#iU', "$1>", $this->_cleaned);
  823. $this->_cleaned = preg_replace('#(<[^>]+)style[\x00-\x20]*=[\x00-\x20]*([\`\'\"]*).*behaviour[\x00-\x20]*\([^>]*>#iU', "$1>", $this->_cleaned);
  824. $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);
  825. $this->_cleaned = preg_replace('#</*\w+:\w[^>]*>#i', "", $this->_cleaned);
  826. do {
  827. $oldstring = $this->_cleaned;
  828. $this->_cleaned = preg_replace('#</*(applet|meta|xml|blink|link|style|script|embed|object|iframe|frame|frameset|ilayer|layer|bgsound|title|base)[^>]*>#i', "", $this->_cleaned);
  829. } while ($oldstring != $this->_cleaned);
  830. $this->_cleaned = str_replace(array("&amp;", "&lt;", "&gt;"), array("&amp;amp;", "&amp;lt;", "&amp;gt;"), $this->_cleaned);
  831. }
  832. }