Helper.php 26 KB

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