Helper.php 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914
  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. App::uses('Hash', 'Utility');
  17. /**
  18. * Abstract base class for all other Helpers in CakePHP.
  19. * Provides common methods and features.
  20. *
  21. * @package Cake.View
  22. */
  23. class Helper extends Object {
  24. /**
  25. * List of helpers used by this helper
  26. *
  27. * @var array
  28. */
  29. public $helpers = array();
  30. /**
  31. * A helper lookup table used to lazy load helper objects.
  32. *
  33. * @var array
  34. */
  35. protected $_helperMap = array();
  36. /**
  37. * The current theme name if any.
  38. *
  39. * @var string
  40. */
  41. public $theme = null;
  42. /**
  43. * Request object
  44. *
  45. * @var CakeRequest
  46. */
  47. public $request = null;
  48. /**
  49. * Plugin path
  50. *
  51. * @var string
  52. */
  53. public $plugin = null;
  54. /**
  55. * Holds the fields array('field_name' => array('type' => 'string', 'length' => 100),
  56. * primaryKey and validates array('field_name')
  57. *
  58. * @var array
  59. */
  60. public $fieldset = array();
  61. /**
  62. * Holds tag templates.
  63. *
  64. * @var array
  65. */
  66. public $tags = array();
  67. /**
  68. * Holds the content to be cleaned.
  69. *
  70. * @var mixed
  71. */
  72. protected $_tainted = null;
  73. /**
  74. * Holds the cleaned content.
  75. *
  76. * @var mixed
  77. */
  78. protected $_cleaned = null;
  79. /**
  80. * The View instance this helper is attached to
  81. *
  82. * @var View
  83. */
  84. protected $_View;
  85. /**
  86. * A list of strings that should be treated as suffixes, or
  87. * sub inputs for a parent input. This is used for date/time
  88. * inputs primarily.
  89. *
  90. * @var array
  91. */
  92. protected $_fieldSuffixes = array(
  93. 'year', 'month', 'day', 'hour', 'min', 'second', 'meridian'
  94. );
  95. /**
  96. * The name of the current model entities are in scope of.
  97. *
  98. * @see Helper::setEntity()
  99. * @var string
  100. */
  101. protected $_modelScope;
  102. /**
  103. * The name of the current model association entities are in scope of.
  104. *
  105. * @see Helper::setEntity()
  106. * @var string
  107. */
  108. protected $_association;
  109. /**
  110. * The dot separated list of elements the current field entity is for.
  111. *
  112. * @see Helper::setEntity()
  113. * @var string
  114. */
  115. protected $_entityPath;
  116. /**
  117. * Minimized attributes
  118. *
  119. * @var array
  120. */
  121. protected $_minimizedAttributes = array(
  122. 'compact', 'checked', 'declare', 'readonly', 'disabled', 'selected',
  123. 'defer', 'ismap', 'nohref', 'noshade', 'nowrap', 'multiple', 'noresize',
  124. 'autoplay', 'controls', 'loop', 'muted'
  125. );
  126. /**
  127. * Format to attribute
  128. *
  129. * @var string
  130. */
  131. protected $_attributeFormat = '%s="%s"';
  132. /**
  133. * Format to attribute
  134. *
  135. * @var string
  136. */
  137. protected $_minimizedAttributeFormat = '%s="%s"';
  138. /**
  139. * Default Constructor
  140. *
  141. * @param View $View The View this helper is being attached to.
  142. * @param array $settings Configuration settings for the helper.
  143. */
  144. public function __construct(View $View, $settings = array()) {
  145. $this->_View = $View;
  146. $this->request = $View->request;
  147. if (!empty($this->helpers)) {
  148. $this->_helperMap = ObjectCollection::normalizeObjectArray($this->helpers);
  149. }
  150. }
  151. /**
  152. * Provide non fatal errors on missing method calls.
  153. *
  154. * @param string $method Method to invoke
  155. * @param array $params Array of params for the method.
  156. * @return void
  157. */
  158. public function __call($method, $params) {
  159. trigger_error(__d('cake_dev', 'Method %1$s::%2$s does not exist', get_class($this), $method), E_USER_WARNING);
  160. }
  161. /**
  162. * Lazy loads helpers. Provides access to deprecated request properties as well.
  163. *
  164. * @param string $name Name of the property being accessed.
  165. * @return mixed Helper or property found at $name
  166. */
  167. public function __get($name) {
  168. if (isset($this->_helperMap[$name]) && !isset($this->{$name})) {
  169. $settings = array_merge((array)$this->_helperMap[$name]['settings'], array('enabled' => false));
  170. $this->{$name} = $this->_View->loadHelper($this->_helperMap[$name]['class'], $settings);
  171. }
  172. if (isset($this->{$name})) {
  173. return $this->{$name};
  174. }
  175. switch ($name) {
  176. case 'base':
  177. case 'here':
  178. case 'webroot':
  179. case 'data':
  180. return $this->request->{$name};
  181. case 'action':
  182. return isset($this->request->params['action']) ? $this->request->params['action'] : '';
  183. case 'params':
  184. return $this->request;
  185. }
  186. }
  187. /**
  188. * Provides backwards compatibility access for setting values to the request object.
  189. *
  190. * @param string $name Name of the property being accessed.
  191. * @param mixed $value
  192. * @return mixed Return the $value
  193. */
  194. public function __set($name, $value) {
  195. switch ($name) {
  196. case 'base':
  197. case 'here':
  198. case 'webroot':
  199. case 'data':
  200. return $this->request->{$name} = $value;
  201. case 'action':
  202. return $this->request->params['action'] = $value;
  203. }
  204. return $this->{$name} = $value;
  205. }
  206. /**
  207. * Finds URL for specified action.
  208. *
  209. * Returns a URL pointing at the provided parameters.
  210. *
  211. * @param string|array $url Either a relative string url like `/products/view/23` or
  212. * an array of url parameters. Using an array for urls will allow you to leverage
  213. * the reverse routing features of CakePHP.
  214. * @param boolean $full If true, the full base URL will be prepended to the result
  215. * @return string Full translated URL with base path.
  216. * @link http://book.cakephp.org/2.0/en/views/helpers.html
  217. */
  218. public function url($url = null, $full = false) {
  219. return h(Router::url($url, $full));
  220. }
  221. /**
  222. * Checks if a file exists when theme is used, if no file is found default location is returned
  223. *
  224. * @param string $file The file to create a webroot path to.
  225. * @return string Web accessible path to file.
  226. */
  227. public function webroot($file) {
  228. $asset = explode('?', $file);
  229. $asset[1] = isset($asset[1]) ? '?' . $asset[1] : null;
  230. $webPath = "{$this->request->webroot}" . $asset[0];
  231. $file = $asset[0];
  232. if (!empty($this->theme)) {
  233. $file = trim($file, '/');
  234. $theme = $this->theme . '/';
  235. if (DS === '\\') {
  236. $file = str_replace('/', '\\', $file);
  237. }
  238. if (file_exists(Configure::read('App.www_root') . 'theme' . DS . $this->theme . DS . $file)) {
  239. $webPath = "{$this->request->webroot}theme/" . $theme . $asset[0];
  240. } else {
  241. $themePath = App::themePath($this->theme);
  242. $path = $themePath . 'webroot' . DS . $file;
  243. if (file_exists($path)) {
  244. $webPath = "{$this->request->webroot}theme/" . $theme . $asset[0];
  245. }
  246. }
  247. }
  248. if (strpos($webPath, '//') !== false) {
  249. return str_replace('//', '/', $webPath . $asset[1]);
  250. }
  251. return $webPath . $asset[1];
  252. }
  253. /**
  254. * Generate url for given asset file. Depending on options passed provides full url with domain name.
  255. * Also calls Helper::assetTimestamp() to add timestamp to local files
  256. *
  257. * @param string|array Path string or url array
  258. * @param array $options Options array. Possible keys:
  259. * `fullBase` Return full url with domain name
  260. * `pathPrefix` Path prefix for relative urls
  261. * `ext` Asset extension to append
  262. * `plugin` False value will prevent parsing path as a plugin
  263. * @return string Generated url
  264. */
  265. public function assetUrl($path, $options = array()) {
  266. if (is_array($path)) {
  267. $path = $this->url($path, !empty($options['fullBase']));
  268. } elseif (strpos($path, '://') === false) {
  269. if (!array_key_exists('plugin', $options) || $options['plugin'] !== false) {
  270. list($plugin, $path) = $this->_View->pluginSplit($path, false);
  271. }
  272. if (!empty($options['pathPrefix']) && $path[0] !== '/') {
  273. $path = $options['pathPrefix'] . $path;
  274. }
  275. if (
  276. !empty($options['ext']) &&
  277. strpos($path, '?') === false &&
  278. substr($path, -strlen($options['ext'])) !== $options['ext']
  279. ) {
  280. $path .= $options['ext'];
  281. }
  282. if (isset($plugin)) {
  283. $path = Inflector::underscore($plugin) . '/' . $path;
  284. }
  285. $path = h($this->assetTimestamp($this->webroot($path)));
  286. if (!empty($options['fullBase'])) {
  287. $base = $this->url('/', true);
  288. $len = strlen($this->request->webroot);
  289. if ($len) {
  290. $base = substr($base, 0, -$len);
  291. }
  292. $path = $base . $path;
  293. }
  294. }
  295. return $path;
  296. }
  297. /**
  298. * Adds a timestamp to a file based resource based on the value of `Asset.timestamp` in
  299. * Configure. If Asset.timestamp is true and debug > 0, or Asset.timestamp == 'force'
  300. * a timestamp will be added.
  301. *
  302. * @param string $path The file path to timestamp, the path must be inside WWW_ROOT
  303. * @return string Path with a timestamp added, or not.
  304. */
  305. public function assetTimestamp($path) {
  306. $stamp = Configure::read('Asset.timestamp');
  307. $timestampEnabled = $stamp === 'force' || ($stamp === true && Configure::read('debug') > 0);
  308. if ($timestampEnabled && strpos($path, '?') === false) {
  309. $filepath = preg_replace('/^' . preg_quote($this->request->webroot, '/') . '/', '', $path);
  310. $webrootPath = WWW_ROOT . str_replace('/', DS, $filepath);
  311. if (file_exists($webrootPath)) {
  312. return $path . '?' . @filemtime($webrootPath);
  313. }
  314. $segments = explode('/', ltrim($filepath, '/'));
  315. if ($segments[0] === 'theme') {
  316. $theme = $segments[1];
  317. unset($segments[0], $segments[1]);
  318. $themePath = App::themePath($theme) . 'webroot' . DS . implode(DS, $segments);
  319. return $path . '?' . @filemtime($themePath);
  320. } else {
  321. $plugin = Inflector::camelize($segments[0]);
  322. if (CakePlugin::loaded($plugin)) {
  323. unset($segments[0]);
  324. $pluginPath = CakePlugin::path($plugin) . 'webroot' . DS . implode(DS, $segments);
  325. return $path . '?' . @filemtime($pluginPath);
  326. }
  327. }
  328. }
  329. return $path;
  330. }
  331. /**
  332. * Used to remove harmful tags from content. Removes a number of well known XSS attacks
  333. * from content. However, is not guaranteed to remove all possibilities. Escaping
  334. * content is the best way to prevent all possible attacks.
  335. *
  336. * @param string|array $output Either an array of strings to clean or a single string to clean.
  337. * @return string|array cleaned content for output
  338. */
  339. public function clean($output) {
  340. $this->_reset();
  341. if (empty($output)) {
  342. return null;
  343. }
  344. if (is_array($output)) {
  345. foreach ($output as $key => $value) {
  346. $return[$key] = $this->clean($value);
  347. }
  348. return $return;
  349. }
  350. $this->_tainted = $output;
  351. $this->_clean();
  352. return $this->_cleaned;
  353. }
  354. /**
  355. * Returns a space-delimited string with items of the $options array. If a
  356. * key of $options array happens to be one of:
  357. *
  358. * - 'compact'
  359. * - 'checked'
  360. * - 'declare'
  361. * - 'readonly'
  362. * - 'disabled'
  363. * - 'selected'
  364. * - 'defer'
  365. * - 'ismap'
  366. * - 'nohref'
  367. * - 'noshade'
  368. * - 'nowrap'
  369. * - 'multiple'
  370. * - 'noresize'
  371. *
  372. * And its value is one of:
  373. *
  374. * - '1' (string)
  375. * - 1 (integer)
  376. * - true (boolean)
  377. * - 'true' (string)
  378. *
  379. * Then the value will be reset to be identical with key's name.
  380. * If the value is not one of these 3, the parameter is not output.
  381. *
  382. * 'escape' is a special option in that it controls the conversion of
  383. * attributes to their html-entity encoded equivalents. Set to false to disable html-encoding.
  384. *
  385. * If value for any option key is set to `null` or `false`, that option will be excluded from output.
  386. *
  387. * @param array $options Array of options.
  388. * @param array $exclude Array of options to be excluded, the options here will not be part of the return.
  389. * @param string $insertBefore String to be inserted before options.
  390. * @param string $insertAfter String to be inserted after options.
  391. * @return string Composed attributes.
  392. * @deprecated This method will be moved to HtmlHelper in 3.0
  393. */
  394. protected function _parseAttributes($options, $exclude = null, $insertBefore = ' ', $insertAfter = null) {
  395. if (!is_string($options)) {
  396. $options = (array)$options + array('escape' => true);
  397. if (!is_array($exclude)) {
  398. $exclude = array();
  399. }
  400. $exclude = array('escape' => true) + array_flip($exclude);
  401. $escape = $options['escape'];
  402. $attributes = array();
  403. foreach ($options as $key => $value) {
  404. if (!isset($exclude[$key]) && $value !== false && $value !== null) {
  405. $attributes[] = $this->_formatAttribute($key, $value, $escape);
  406. }
  407. }
  408. $out = implode(' ', $attributes);
  409. } else {
  410. $out = $options;
  411. }
  412. return $out ? $insertBefore . $out . $insertAfter : '';
  413. }
  414. /**
  415. * Formats an individual attribute, and returns the string value of the composed attribute.
  416. * Works with minimized attributes that have the same value as their name such as 'disabled' and 'checked'
  417. *
  418. * @param string $key The name of the attribute to create
  419. * @param string $value The value of the attribute to create.
  420. * @param boolean $escape Define if the value must be escaped
  421. * @return string The composed attribute.
  422. * @deprecated This method will be moved to HtmlHelper in 3.0
  423. */
  424. protected function _formatAttribute($key, $value, $escape = true) {
  425. $attribute = '';
  426. if (is_array($value)) {
  427. $value = implode(' ' , $value);
  428. }
  429. if (is_numeric($key)) {
  430. $attribute = sprintf($this->_minimizedAttributeFormat, $value, $value);
  431. } elseif (in_array($key, $this->_minimizedAttributes)) {
  432. if ($value === 1 || $value === true || $value === 'true' || $value === '1' || $value == $key) {
  433. $attribute = sprintf($this->_minimizedAttributeFormat, $key, $key);
  434. }
  435. } else {
  436. $attribute = sprintf($this->_attributeFormat, $key, ($escape ? h($value) : $value));
  437. }
  438. return $attribute;
  439. }
  440. /**
  441. * Sets this helper's model and field properties to the dot-separated value-pair in $entity.
  442. *
  443. * @param string $entity A field name, like "ModelName.fieldName" or "ModelName.ID.fieldName"
  444. * @param boolean $setScope Sets the view scope to the model specified in $tagValue
  445. * @return void
  446. */
  447. public function setEntity($entity, $setScope = false) {
  448. if ($entity === null) {
  449. $this->_modelScope = false;
  450. }
  451. if ($setScope === true) {
  452. $this->_modelScope = $entity;
  453. }
  454. $parts = array_values(Hash::filter(explode('.', $entity)));
  455. if (empty($parts)) {
  456. return;
  457. }
  458. $count = count($parts);
  459. $lastPart = isset($parts[$count - 1]) ? $parts[$count - 1] : null;
  460. // Either 'body' or 'date.month' type inputs.
  461. if (
  462. ($count === 1 && $this->_modelScope && !$setScope) ||
  463. (
  464. $count === 2 &&
  465. in_array($lastPart, $this->_fieldSuffixes) &&
  466. $this->_modelScope &&
  467. $parts[0] !== $this->_modelScope
  468. )
  469. ) {
  470. $entity = $this->_modelScope . '.' . $entity;
  471. }
  472. // 0.name, 0.created.month style inputs. Excludes inputs with the modelScope in them.
  473. if (
  474. $count >= 2 &&
  475. is_numeric($parts[0]) &&
  476. !is_numeric($parts[1]) &&
  477. $this->_modelScope &&
  478. strpos($entity, $this->_modelScope) === false
  479. ) {
  480. $entity = $this->_modelScope . '.' . $entity;
  481. }
  482. $this->_association = null;
  483. $isHabtm = (
  484. isset($this->fieldset[$this->_modelScope]['fields'][$parts[0]]['type']) &&
  485. $this->fieldset[$this->_modelScope]['fields'][$parts[0]]['type'] === 'multiple'
  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. }