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