1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14:
15:
16: App::uses('Router', 'Routing');
17:
18: 19: 20: 21: 22: 23:
24: class Helper extends Object {
25:
26: 27: 28: 29: 30:
31: public $helpers = array();
32:
33: 34: 35: 36: 37:
38: protected $_helperMap = array();
39:
40: 41: 42: 43: 44:
45: public $theme = null;
46:
47: 48: 49: 50: 51:
52: public $request = null;
53:
54: 55: 56: 57: 58:
59: public $plugin = null;
60:
61: 62: 63: 64: 65: 66:
67: public $fieldset = array();
68:
69: 70: 71: 72: 73:
74: public $tags = array();
75:
76: 77: 78: 79: 80:
81: protected $_tainted = null;
82:
83: 84: 85: 86: 87:
88: protected $_cleaned = null;
89:
90: 91: 92: 93: 94:
95: protected $_View;
96:
97: 98: 99: 100: 101: 102: 103:
104: protected $_fieldSuffixes = array(
105: 'year', 'month', 'day', 'hour', 'min', 'second', 'meridian'
106: );
107:
108: 109: 110: 111: 112: 113:
114: protected $_modelScope;
115:
116: 117: 118: 119: 120: 121:
122: protected $_association;
123:
124: 125: 126: 127: 128: 129:
130: protected $_entityPath;
131:
132: 133: 134: 135: 136:
137: protected $_minimizedAttributes = array(
138: 'compact', 'checked', 'declare', 'readonly', 'disabled', 'selected',
139: 'defer', 'ismap', 'nohref', 'noshade', 'nowrap', 'multiple', 'noresize',
140: 'autoplay', 'controls', 'loop', 'muted'
141: );
142:
143: 144: 145: 146: 147:
148: protected $_attributeFormat = '%s="%s"';
149:
150: 151: 152: 153: 154:
155: protected $_minimizedAttributeFormat = '%s="%s"';
156:
157: 158: 159: 160: 161: 162:
163: public function __construct(View $View, $settings = array()) {
164: $this->_View = $View;
165: $this->request = $View->request;
166: if (!empty($this->helpers)) {
167: $this->_helperMap = ObjectCollection::normalizeObjectArray($this->helpers);
168: }
169: }
170:
171: 172: 173: 174: 175: 176: 177:
178: public function __call($method, $params) {
179: trigger_error(__d('cake_dev', 'Method %1$s::%2$s does not exist', get_class($this), $method), E_USER_WARNING);
180: }
181:
182: 183: 184: 185: 186: 187:
188: public function __get($name) {
189: if (isset($this->_helperMap[$name]) && !isset($this->{$name})) {
190: $settings = array_merge((array)$this->_helperMap[$name]['settings'], array('enabled' => false));
191: $this->{$name} = $this->_View->loadHelper($this->_helperMap[$name]['class'], $settings);
192: }
193: if (isset($this->{$name})) {
194: return $this->{$name};
195: }
196: switch ($name) {
197: case 'base':
198: case 'here':
199: case 'webroot':
200: case 'data':
201: return $this->request->{$name};
202: case 'action':
203: return isset($this->request->params['action']) ? $this->request->params['action'] : '';
204: case 'params':
205: return $this->request;
206: }
207: }
208:
209: 210: 211: 212: 213: 214: 215:
216: public function __set($name, $value) {
217: switch ($name) {
218: case 'base':
219: case 'here':
220: case 'webroot':
221: case 'data':
222: return $this->request->{$name} = $value;
223: case 'action':
224: return $this->request->params['action'] = $value;
225: }
226: return $this->{$name} = $value;
227: }
228:
229: 230: 231: 232: 233: 234: 235: 236: 237: 238: 239: 240:
241: public function url($url = null, $full = false) {
242: return h(Router::url($url, $full));
243: }
244:
245: 246: 247: 248: 249: 250:
251: public function webroot($file) {
252: $asset = explode('?', $file);
253: $asset[1] = isset($asset[1]) ? '?' . $asset[1] : null;
254: $webPath = "{$this->request->webroot}" . $asset[0];
255: $file = $asset[0];
256:
257: if (!empty($this->theme)) {
258: $file = trim($file, '/');
259: $theme = $this->theme . '/';
260:
261: if (DS === '\\') {
262: $file = str_replace('/', '\\', $file);
263: }
264:
265: if (file_exists(Configure::read('App.www_root') . 'theme' . DS . $this->theme . DS . $file)) {
266: $webPath = "{$this->request->webroot}theme/" . $theme . $asset[0];
267: } else {
268: $themePath = App::themePath($this->theme);
269: $path = $themePath . 'webroot' . DS . $file;
270: if (file_exists($path)) {
271: $webPath = "{$this->request->webroot}theme/" . $theme . $asset[0];
272: }
273: }
274: }
275: if (strpos($webPath, '//') !== false) {
276: return str_replace('//', '/', $webPath . $asset[1]);
277: }
278: return $webPath . $asset[1];
279: }
280:
281: 282: 283: 284: 285: 286: 287: 288: 289: 290: 291: 292:
293: public function assetUrl($path, $options = array()) {
294: if (is_array($path)) {
295: $path = $this->url($path, !empty($options['fullBase']));
296: } elseif (strpos($path, '://') === false) {
297: if (!array_key_exists('plugin', $options) || $options['plugin'] !== false) {
298: list($plugin, $path) = $this->_View->pluginSplit($path, false);
299: }
300: if (!empty($options['pathPrefix']) && $path[0] !== '/') {
301: $path = $options['pathPrefix'] . $path;
302: }
303: if (
304: !empty($options['ext']) &&
305: strpos($path, '?') === false &&
306: substr($path, -strlen($options['ext'])) !== $options['ext']
307: ) {
308: $path .= $options['ext'];
309: }
310: if (isset($plugin)) {
311: $path = Inflector::underscore($plugin) . '/' . $path;
312: }
313: $path = h($this->assetTimestamp($this->webroot($path)));
314:
315: if (!empty($options['fullBase'])) {
316: if ($path[0] == '/') {
317: $path = substr($path, 1);
318: }
319: $path = $this->url('/', true) . $path;
320: }
321: }
322:
323: return $path;
324: }
325:
326: 327: 328: 329: 330: 331: 332: 333:
334: public function assetTimestamp($path) {
335: $stamp = Configure::read('Asset.timestamp');
336: $timestampEnabled = $stamp === 'force' || ($stamp === true && Configure::read('debug') > 0);
337: if ($timestampEnabled && strpos($path, '?') === false) {
338: $filepath = preg_replace('/^' . preg_quote($this->request->webroot, '/') . '/', '', $path);
339: $webrootPath = WWW_ROOT . str_replace('/', DS, $filepath);
340: if (file_exists($webrootPath)) {
341: return $path . '?' . @filemtime($webrootPath);
342: }
343: $segments = explode('/', ltrim($filepath, '/'));
344: if ($segments[0] === 'theme') {
345: $theme = $segments[1];
346: unset($segments[0], $segments[1]);
347: $themePath = App::themePath($theme) . 'webroot' . DS . implode(DS, $segments);
348: return $path . '?' . @filemtime($themePath);
349: } else {
350: $plugin = Inflector::camelize($segments[0]);
351: if (CakePlugin::loaded($plugin)) {
352: unset($segments[0]);
353: $pluginPath = CakePlugin::path($plugin) . 'webroot' . DS . implode(DS, $segments);
354: return $path . '?' . @filemtime($pluginPath);
355: }
356: }
357: }
358: return $path;
359: }
360:
361: 362: 363: 364: 365: 366: 367: 368:
369: public function clean($output) {
370: $this->_reset();
371: if (empty($output)) {
372: return null;
373: }
374: if (is_array($output)) {
375: foreach ($output as $key => $value) {
376: $return[$key] = $this->clean($value);
377: }
378: return $return;
379: }
380: $this->_tainted = $output;
381: $this->_clean();
382: return $this->_cleaned;
383: }
384:
385: 386: 387: 388: 389: 390: 391: 392: 393: 394: 395: 396: 397: 398: 399: 400: 401: 402: 403: 404: 405: 406: 407: 408: 409: 410: 411: 412: 413: 414: 415: 416: 417: 418: 419: 420: 421: 422: 423: 424:
425: protected function _parseAttributes($options, $exclude = null, $insertBefore = ' ', $insertAfter = null) {
426: if (!is_string($options)) {
427: $options = (array)$options + array('escape' => true);
428:
429: if (!is_array($exclude)) {
430: $exclude = array();
431: }
432:
433: $exclude = array('escape' => true) + array_flip($exclude);
434: $escape = $options['escape'];
435: $attributes = array();
436:
437: foreach ($options as $key => $value) {
438: if (!isset($exclude[$key]) && $value !== false && $value !== null) {
439: $attributes[] = $this->_formatAttribute($key, $value, $escape);
440: }
441: }
442: $out = implode(' ', $attributes);
443: } else {
444: $out = $options;
445: }
446: return $out ? $insertBefore . $out . $insertAfter : '';
447: }
448:
449: 450: 451: 452: 453: 454: 455: 456: 457: 458:
459: protected function _formatAttribute($key, $value, $escape = true) {
460: $attribute = '';
461: if (is_array($value)) {
462: $value = implode(' ' , $value);
463: }
464:
465: if (is_numeric($key)) {
466: $attribute = sprintf($this->_minimizedAttributeFormat, $value, $value);
467: } elseif (in_array($key, $this->_minimizedAttributes)) {
468: if ($value === 1 || $value === true || $value === 'true' || $value === '1' || $value == $key) {
469: $attribute = sprintf($this->_minimizedAttributeFormat, $key, $key);
470: }
471: } else {
472: $attribute = sprintf($this->_attributeFormat, $key, ($escape ? h($value) : $value));
473: }
474: return $attribute;
475: }
476:
477: 478: 479: 480: 481: 482: 483:
484: public function setEntity($entity, $setScope = false) {
485: if ($entity === null) {
486: $this->_modelScope = false;
487: }
488: if ($setScope === true) {
489: $this->_modelScope = $entity;
490: }
491: $parts = array_values(Set::filter(explode('.', $entity), true));
492: if (empty($parts)) {
493: return;
494: }
495: $count = count($parts);
496: $lastPart = isset($parts[$count - 1]) ? $parts[$count - 1] : null;
497:
498:
499: if (
500: ($count === 1 && $this->_modelScope && $setScope == false) ||
501: (
502: $count === 2 &&
503: in_array($lastPart, $this->_fieldSuffixes) &&
504: $this->_modelScope &&
505: $parts[0] !== $this->_modelScope
506: )
507: ) {
508: $entity = $this->_modelScope . '.' . $entity;
509: }
510:
511:
512: if (
513: $count >= 2 &&
514: is_numeric($parts[0]) &&
515: !is_numeric($parts[1]) &&
516: $this->_modelScope &&
517: strpos($entity, $this->_modelScope) === false
518: ) {
519: $entity = $this->_modelScope . '.' . $entity;
520: }
521:
522: $this->_association = null;
523:
524: $isHabtm = (
525: isset($this->fieldset[$this->_modelScope]['fields'][$parts[0]]['type']) &&
526: $this->fieldset[$this->_modelScope]['fields'][$parts[0]]['type'] === 'multiple' &&
527: $count == 1
528: );
529:
530:
531: if ($count == 1 && $isHabtm) {
532: $this->_association = $parts[0];
533: $entity = $parts[0] . '.' . $parts[0];
534: } else {
535:
536: $reversed = array_reverse($parts);
537: foreach ($reversed as $i => $part) {
538: if ($i > 0 && preg_match('/^[A-Z]/', $part)) {
539: $this->_association = $part;
540: break;
541: }
542: }
543: }
544: $this->_entityPath = $entity;
545: }
546:
547: 548: 549: 550: 551:
552: public function entity() {
553: return explode('.', $this->_entityPath);
554: }
555:
556: 557: 558: 559: 560:
561: public function model() {
562: if ($this->_association) {
563: return $this->_association;
564: }
565: return $this->_modelScope;
566: }
567:
568: 569: 570: 571: 572: 573: 574:
575: public function field() {
576: $entity = $this->entity();
577: $count = count($entity);
578: $last = $entity[$count - 1];
579: if ($count > 2 && in_array($last, $this->_fieldSuffixes)) {
580: $last = isset($entity[$count - 2]) ? $entity[$count - 2] : null;
581: }
582: return $last;
583: }
584:
585: 586: 587: 588: 589: 590: 591: 592: 593: 594: 595:
596: public function domId($options = null, $id = 'id') {
597: if (is_array($options) && array_key_exists($id, $options) && $options[$id] === null) {
598: unset($options[$id]);
599: return $options;
600: } elseif (!is_array($options) && $options !== null) {
601: $this->setEntity($options);
602: return $this->domId();
603: }
604:
605: $entity = $this->entity();
606: $model = array_shift($entity);
607: $dom = $model . join('', array_map(array('Inflector', 'camelize'), $entity));
608:
609: if (is_array($options) && !array_key_exists($id, $options)) {
610: $options[$id] = $dom;
611: } elseif ($options === null) {
612: return $dom;
613: }
614: return $options;
615: }
616:
617: 618: 619: 620: 621: 622: 623: 624: 625: 626: 627: 628:
629: protected function _name($options = array(), $field = null, $key = 'name') {
630: if ($options === null) {
631: $options = array();
632: } elseif (is_string($options)) {
633: $field = $options;
634: $options = 0;
635: }
636:
637: if (!empty($field)) {
638: $this->setEntity($field);
639: }
640:
641: if (is_array($options) && array_key_exists($key, $options)) {
642: return $options;
643: }
644:
645: switch ($field) {
646: case '_method':
647: $name = $field;
648: break;
649: default:
650: $name = 'data[' . implode('][', $this->entity()) . ']';
651: break;
652: }
653:
654: if (is_array($options)) {
655: $options[$key] = $name;
656: return $options;
657: } else {
658: return $name;
659: }
660: }
661:
662: 663: 664: 665: 666: 667: 668: 669: 670: 671: 672:
673: public function value($options = array(), $field = null, $key = 'value') {
674: if ($options === null) {
675: $options = array();
676: } elseif (is_string($options)) {
677: $field = $options;
678: $options = 0;
679: }
680:
681: if (is_array($options) && isset($options[$key])) {
682: return $options;
683: }
684:
685: if (!empty($field)) {
686: $this->setEntity($field);
687: }
688: $result = null;
689: $data = $this->request->data;
690:
691: $entity = $this->entity();
692: if (!empty($data) && !empty($entity)) {
693: $result = Set::extract(implode('.', $entity), $data);
694: }
695:
696: $habtmKey = $this->field();
697: if (empty($result) && isset($data[$habtmKey][$habtmKey]) && is_array($data[$habtmKey])) {
698: $result = $data[$habtmKey][$habtmKey];
699: } elseif (empty($result) && isset($data[$habtmKey]) && is_array($data[$habtmKey])) {
700: if (ClassRegistry::isKeySet($habtmKey)) {
701: $model = ClassRegistry::getObject($habtmKey);
702: $result = $this->_selectedArray($data[$habtmKey], $model->primaryKey);
703: }
704: }
705:
706: if (is_array($options)) {
707: if ($result === null && isset($options['default'])) {
708: $result = $options['default'];
709: }
710: unset($options['default']);
711: }
712:
713: if (is_array($options)) {
714: $options[$key] = $result;
715: return $options;
716: } else {
717: return $result;
718: }
719: }
720:
721: 722: 723: 724: 725: 726: 727: 728: 729:
730: protected function _initInputField($field, $options = array()) {
731: if ($field !== null) {
732: $this->setEntity($field);
733: }
734: $options = (array)$options;
735: $options = $this->_name($options);
736: $options = $this->value($options);
737: $options = $this->domId($options);
738: return $options;
739: }
740:
741: 742: 743: 744: 745: 746: 747: 748:
749: public function addClass($options = array(), $class = null, $key = 'class') {
750: if (isset($options[$key]) && trim($options[$key]) != '') {
751: $options[$key] .= ' ' . $class;
752: } else {
753: $options[$key] = $class;
754: }
755: return $options;
756: }
757:
758: 759: 760: 761: 762: 763: 764: 765: 766:
767: public function output($str) {
768: return $str;
769: }
770:
771: 772: 773: 774: 775: 776: 777: 778:
779: public function beforeRender($viewFile) {
780: }
781:
782: 783: 784: 785: 786: 787: 788: 789: 790:
791: public function afterRender($viewFile) {
792: }
793:
794: 795: 796: 797: 798: 799: 800: 801:
802: public function beforeLayout($layoutFile) {
803: }
804:
805: 806: 807: 808: 809: 810: 811: 812:
813: public function afterLayout($layoutFile) {
814: }
815:
816: 817: 818: 819: 820: 821: 822: 823: 824:
825: public function beforeRenderFile($viewfile) {
826: }
827:
828: 829: 830: 831: 832: 833: 834: 835: 836: 837:
838: public function afterRenderFile($viewfile, $content) {
839: }
840:
841: 842: 843: 844: 845: 846: 847: 848:
849: protected function _selectedArray($data, $key = 'id') {
850: if (!is_array($data)) {
851: $model = $data;
852: if (!empty($this->request->data[$model][$model])) {
853: return $this->request->data[$model][$model];
854: }
855: if (!empty($this->request->data[$model])) {
856: $data = $this->request->data[$model];
857: }
858: }
859: $array = array();
860: if (!empty($data)) {
861: foreach ($data as $row) {
862: if (isset($row[$key])) {
863: $array[$row[$key]] = $row[$key];
864: }
865: }
866: }
867: return empty($array) ? null : $array;
868: }
869:
870: 871: 872: 873: 874:
875: protected function _reset() {
876: $this->_tainted = null;
877: $this->_cleaned = null;
878: }
879:
880: 881: 882: 883: 884:
885: protected function _clean() {
886: if (get_magic_quotes_gpc()) {
887: $this->_cleaned = stripslashes($this->_tainted);
888: } else {
889: $this->_cleaned = $this->_tainted;
890: }
891:
892: $this->_cleaned = str_replace(array("&", "<", ">"), array("&amp;", "&lt;", "&gt;"), $this->_cleaned);
893: $this->_cleaned = preg_replace('#(&\#*\w+)[\x00-\x20]+;#u', "$1;", $this->_cleaned);
894: $this->_cleaned = preg_replace('#(&\#x*)([0-9A-F]+);*#iu', "$1$2;", $this->_cleaned);
895: $this->_cleaned = html_entity_decode($this->_cleaned, ENT_COMPAT, "UTF-8");
896: $this->_cleaned = preg_replace('#(<[^>]+[\x00-\x20\"\'\/])(on|xmlns)[^>]*>#iUu', "$1>", $this->_cleaned);
897: $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);
898: $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);
899: $this->_cleaned = preg_replace('#([a-z]*)[\x00-\x20]*=*([\'\"]*)[\x00-\x20]*-moz-binding[\x00-\x20]*:#iUu', '$1=$2nomozbinding...', $this->_cleaned);
900: $this->_cleaned = preg_replace('#([a-z]*)[\x00-\x20]*=([\'\"]*)[\x00-\x20]*data[\x00-\x20]*:#Uu', '$1=$2nodata...', $this->_cleaned);
901: $this->_cleaned = preg_replace('#(<[^>]+)style[\x00-\x20]*=[\x00-\x20]*([\`\'\"]*).*expression[\x00-\x20]*\([^>]*>#iU', "$1>", $this->_cleaned);
902: $this->_cleaned = preg_replace('#(<[^>]+)style[\x00-\x20]*=[\x00-\x20]*([\`\'\"]*).*behaviour[\x00-\x20]*\([^>]*>#iU', "$1>", $this->_cleaned);
903: $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);
904: $this->_cleaned = preg_replace('#</*\w+:\w[^>]*>#i', "", $this->_cleaned);
905: do {
906: $oldstring = $this->_cleaned;
907: $this->_cleaned = preg_replace('#</*(applet|meta|xml|blink|link|style|script|embed|object|iframe|frame|frameset|ilayer|layer|bgsound|title|base)[^>]*>#i', "", $this->_cleaned);
908: } while ($oldstring != $this->_cleaned);
909: $this->_cleaned = str_replace(array("&", "<", ">"), array("&amp;", "&lt;", "&gt;"), $this->_cleaned);
910: }
911:
912: }
913: