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:
27: App::import('Core', array('Helper', 'ClassRegistry'));
28: 29: 30: 31: 32: 33: 34: 35:
36: class View extends Object {
37: 38: 39: 40: 41: 42:
43: var $base = null;
44: 45: 46: 47: 48:
49: var $here = null;
50: 51: 52: 53: 54: 55:
56: var $plugin = null;
57: 58: 59: 60: 61: 62:
63: var $name = null;
64: 65: 66: 67: 68: 69:
70: var $action = null;
71: 72: 73: 74: 75:
76: var $params = array();
77: 78: 79: 80: 81:
82: var $passedArgs = array();
83: 84: 85: 86: 87:
88: var $data = array();
89: 90: 91: 92: 93: 94:
95: var $helpers = array('Html');
96: 97: 98: 99: 100:
101: var $viewPath = null;
102: 103: 104: 105: 106: 107:
108: var $viewVars = array();
109: 110: 111: 112: 113: 114:
115: var $layout = 'default';
116: 117: 118: 119: 120:
121: var $layoutPath = null;
122: 123: 124: 125: 126: 127:
128: var $pageTitle = false;
129: 130: 131: 132: 133: 134:
135: var $autoRender = true;
136: 137: 138: 139: 140: 141:
142: var $autoLayout = true;
143: 144: 145: 146: 147:
148: var $ext = '.ctp';
149: 150: 151: 152: 153:
154: var $subDir = null;
155: 156: 157: 158: 159:
160: var $themeWeb = null;
161: 162: 163: 164: 165: 166: 167:
168: var $cacheAction = false;
169: 170: 171: 172: 173:
174: var $validationErrors = array();
175: 176: 177: 178: 179:
180: var $hasRendered = false;
181: 182: 183: 184: 185:
186: var $loaded = array();
187: 188: 189: 190: 191:
192: var $modelScope = false;
193: 194: 195: 196: 197:
198: var $model = null;
199: 200: 201: 202: 203:
204: var $association = null;
205: 206: 207: 208: 209:
210: var $field = null;
211: 212: 213: 214: 215:
216: var $fieldSuffix = null;
217: 218: 219: 220: 221:
222: var $modelId = null;
223: 224: 225: 226: 227:
228: var $uuids = array();
229: 230: 231: 232: 233:
234: var $output = false;
235: 236: 237: 238: 239: 240:
241: var $__passedVars = array(
242: 'viewVars', 'action', 'autoLayout', 'autoRender', 'ext', 'base', 'webroot',
243: 'helpers', 'here', 'layout', 'name', 'pageTitle', 'layoutPath', 'viewPath',
244: 'params', 'data', 'plugin', 'passedArgs', 'cacheAction'
245: );
246: 247: 248: 249: 250: 251:
252: var $__scripts = array();
253: 254: 255: 256: 257:
258: var $__paths = array();
259: 260: 261: 262: 263:
264: function __construct(&$controller, $register = true) {
265: if (is_object($controller)) {
266: $count = count($this->__passedVars);
267: for ($j = 0; $j < $count; $j++) {
268: $var = $this->__passedVars[$j];
269: $this->{$var} = $controller->{$var};
270: }
271: }
272: parent::__construct();
273:
274: if ($register) {
275: ClassRegistry::addObject('view', $this);
276: }
277: }
278: 279: 280: 281: 282: 283: 284: 285: 286: 287: 288: 289: 290: 291: 292: 293: 294: 295: 296:
297: function element($name, $params = array(), $loadHelpers = false) {
298: $file = $plugin = $key = null;
299:
300: if (isset($params['plugin'])) {
301: $plugin = $params['plugin'];
302: }
303:
304: if (isset($this->plugin) && !$plugin) {
305: $plugin = $this->plugin;
306: }
307:
308: if (isset($params['cache'])) {
309: $expires = '+1 day';
310:
311: if (is_array($params['cache'])) {
312: $expires = $params['cache']['time'];
313: $key = Inflector::slug($params['cache']['key']);
314: } elseif ($params['cache'] !== true) {
315: $expires = $params['cache'];
316: $key = implode('_', array_keys($params));
317: }
318:
319: if ($expires) {
320: $cacheFile = 'element_' . $key . '_' . $plugin . Inflector::slug($name);
321: $cache = cache('views' . DS . $cacheFile, null, $expires);
322:
323: if (is_string($cache)) {
324: return $cache;
325: }
326: }
327: }
328: $paths = $this->_paths($plugin);
329:
330: foreach ($paths as $path) {
331: if (file_exists($path . 'elements' . DS . $name . $this->ext)) {
332: $file = $path . 'elements' . DS . $name . $this->ext;
333: break;
334: } elseif (file_exists($path . 'elements' . DS . $name . '.thtml')) {
335: $file = $path . 'elements' . DS . $name . '.thtml';
336: break;
337: }
338: }
339:
340: if (is_file($file)) {
341: $params = array_merge_recursive($params, $this->loaded);
342: $element = $this->_render($file, array_merge($this->viewVars, $params), $loadHelpers);
343: if (isset($params['cache']) && isset($cacheFile) && isset($expires)) {
344: cache('views' . DS . $cacheFile, $element, $expires);
345: }
346: return $element;
347: }
348: $file = $paths[0] . 'elements' . DS . $name . $this->ext;
349:
350: if (Configure::read() > 0) {
351: return "Not Found: " . $file;
352: }
353: }
354: 355: 356: 357: 358: 359: 360: 361: 362:
363: function render($action = null, $layout = null, $file = null) {
364: if ($this->hasRendered) {
365: return true;
366: }
367: $out = null;
368:
369: if ($file != null) {
370: $action = $file;
371: }
372:
373: if ($action !== false && $viewFileName = $this->_getViewFileName($action)) {
374: if (substr($viewFileName, -3) === 'ctp' || substr($viewFileName, -5) === 'thtml') {
375: $out = View::_render($viewFileName, $this->viewVars);
376: } else {
377: $out = $this->_render($viewFileName, $this->viewVars);
378: }
379: }
380:
381: if ($layout === null) {
382: $layout = $this->layout;
383: }
384:
385: if ($out !== false) {
386: if ($layout && $this->autoLayout) {
387: $out = $this->renderLayout($out, $layout);
388: $isCached = (
389: isset($this->loaded['cache']) ||
390: Configure::read('Cache.check') === true
391: );
392:
393: if ($isCached) {
394: $replace = array('<cake:nocache>', '</cake:nocache>');
395: $out = str_replace($replace, '', $out);
396: }
397: }
398: $this->hasRendered = true;
399: } else {
400: $out = $this->_render($viewFileName, $this->viewVars);
401: $msg = __("Error in view %s, got: <blockquote>%s</blockquote>", true);
402: trigger_error(sprintf($msg, $viewFileName, $out), E_USER_ERROR);
403: }
404: return $out;
405: }
406: 407: 408: 409: 410: 411: 412: 413: 414: 415: 416:
417: function renderLayout($content_for_layout, $layout = null) {
418: $layoutFileName = $this->_getLayoutFileName($layout);
419: if (empty($layoutFileName)) {
420: return $this->output;
421: }
422:
423: $debug = '';
424:
425: if (isset($this->viewVars['cakeDebug']) && Configure::read() > 2) {
426: $params = array('controller' => $this->viewVars['cakeDebug']);
427: $debug = View::element('dump', $params, false);
428: unset($this->viewVars['cakeDebug']);
429: }
430:
431: if ($this->pageTitle !== false) {
432: $pageTitle = $this->pageTitle;
433: } else {
434: $pageTitle = Inflector::humanize($this->viewPath);
435: }
436: $data_for_layout = array_merge($this->viewVars, array(
437: 'title_for_layout' => $pageTitle,
438: 'content_for_layout' => $content_for_layout,
439: 'scripts_for_layout' => implode("\n\t", $this->__scripts),
440: 'cakeDebug' => $debug
441: ));
442:
443: if (empty($this->loaded) && !empty($this->helpers)) {
444: $loadHelpers = true;
445: } else {
446: $loadHelpers = false;
447: $data_for_layout = array_merge($data_for_layout, $this->loaded);
448: }
449:
450: $this->_triggerHelpers('beforeLayout');
451:
452: if (substr($layoutFileName, -3) === 'ctp' || substr($layoutFileName, -5) === 'thtml') {
453: $this->output = View::_render($layoutFileName, $data_for_layout, $loadHelpers, true);
454: } else {
455: $this->output = $this->_render($layoutFileName, $data_for_layout, $loadHelpers);
456: }
457:
458: if ($this->output === false) {
459: $this->output = $this->_render($layoutFileName, $data_for_layout);
460: $msg = __("Error in layout %s, got: <blockquote>%s</blockquote>", true);
461: trigger_error(sprintf($msg, $layoutFileName, $this->output), E_USER_ERROR);
462: return false;
463: }
464:
465: $this->_triggerHelpers('afterLayout');
466:
467: return $this->output;
468: }
469: 470: 471: 472: 473: 474: 475:
476: function _triggerHelpers($callback) {
477: if (empty($this->loaded)) {
478: return false;
479: }
480: $helpers = array_keys($this->loaded);
481: foreach ($helpers as $helperName) {
482: $helper =& $this->loaded[$helperName];
483: if (is_object($helper)) {
484: if (is_subclass_of($helper, 'Helper')) {
485: $helper->{$callback}();
486: }
487: }
488: }
489: }
490: 491: 492: 493: 494: 495:
496: function renderCache($filename, $timeStart) {
497: ob_start();
498: include ($filename);
499:
500: if (Configure::read() > 0 && $this->layout != 'xml') {
501: echo "<!-- Cached Render Time: " . round(getMicrotime() - $timeStart, 4) . "s -->";
502: }
503: $out = ob_get_clean();
504:
505: if (preg_match('/^<!--cachetime:(\\d+)-->/', $out, $match)) {
506: if (time() >= $match['1']) {
507: @unlink($filename);
508: unset ($out);
509: return false;
510: } else {
511: if ($this->layout === 'xml') {
512: header('Content-type: text/xml');
513: }
514: echo str_replace('<!--cachetime:' . $match['1'] . '-->', '', $out);
515: return true;
516: }
517: }
518: }
519: 520: 521: 522: 523: 524:
525: function getVars() {
526: return array_keys($this->viewVars);
527: }
528: 529: 530: 531: 532: 533:
534: function getVar($var) {
535: if (!isset($this->viewVars[$var])) {
536: return null;
537: } else {
538: return $this->viewVars[$var];
539: }
540: }
541: 542: 543: 544: 545: 546: 547: 548: 549:
550: function addScript($name, $content = null) {
551: if (empty($content)) {
552: if (!in_array($name, array_values($this->__scripts))) {
553: $this->__scripts[] = $name;
554: }
555: } else {
556: $this->__scripts[$name] = $content;
557: }
558: }
559: 560: 561: 562: 563: 564: 565: 566:
567: function uuid($object, $url) {
568: $c = 1;
569: $url = Router::url($url);
570: $hash = $object . substr(md5($object . $url), 0, 10);
571: while (in_array($hash, $this->uuids)) {
572: $hash = $object . substr(md5($object . $url . $c), 0, 10);
573: $c++;
574: }
575: $this->uuids[] = $hash;
576: return $hash;
577: }
578: 579: 580: 581: 582:
583: function entity() {
584: $assoc = ($this->association) ? $this->association : $this->model;
585: return array_values(Set::filter(
586: array($assoc, $this->modelId, $this->field, $this->fieldSuffix)
587: ));
588: }
589: 590: 591: 592: 593: 594: 595: 596: 597: 598:
599: function set($one, $two = null) {
600: $data = null;
601: if (is_array($one)) {
602: if (is_array($two)) {
603: $data = array_combine($one, $two);
604: } else {
605: $data = $one;
606: }
607: } else {
608: $data = array($one => $two);
609: }
610:
611: if ($data == null) {
612: return false;
613: }
614:
615: foreach ($data as $name => $value) {
616: if ($name == 'title') {
617: $this->pageTitle = $value;
618: } else {
619: $this->viewVars[$name] = $value;
620: }
621: }
622: }
623: 624: 625: 626: 627: 628: 629:
630: function error($code, $name, $message) {
631: header ("HTTP/1.1 {$code} {$name}");
632: print ($this->_render(
633: $this->_getLayoutFileName('error'),
634: array('code' => $code, 'name' => $name, 'message' => $message)
635: ));
636: }
637: 638: 639: 640: 641: 642: 643: 644: 645:
646: function _render($___viewFn, $___dataForView, $loadHelpers = true, $cached = false) {
647: $loadedHelpers = array();
648:
649: if ($this->helpers != false && $loadHelpers === true) {
650: $loadedHelpers = $this->_loadHelpers($loadedHelpers, $this->helpers);
651:
652: foreach (array_keys($loadedHelpers) as $helper) {
653: $camelBackedHelper = Inflector::variable($helper);
654: ${$camelBackedHelper} =& $loadedHelpers[$helper];
655: $this->loaded[$camelBackedHelper] =& ${$camelBackedHelper};
656: }
657:
658: $this->_triggerHelpers('beforeRender');
659: }
660:
661: extract($___dataForView, EXTR_SKIP);
662: ob_start();
663:
664: if (Configure::read() > 0) {
665: include ($___viewFn);
666: } else {
667: @include ($___viewFn);
668: }
669:
670: if ($loadHelpers === true) {
671: $this->_triggerHelpers('afterRender');
672: }
673:
674: $out = ob_get_clean();
675: $caching = (
676: isset($this->loaded['cache']) &&
677: (($this->cacheAction != false)) && (Configure::read('Cache.check') === true)
678: );
679:
680: if ($caching) {
681: if (is_a($this->loaded['cache'], 'CacheHelper')) {
682: $cache =& $this->loaded['cache'];
683: $cache->base = $this->base;
684: $cache->here = $this->here;
685: $cache->helpers = $this->helpers;
686: $cache->action = $this->action;
687: $cache->controllerName = $this->name;
688: $cache->layout = $this->layout;
689: $cache->cacheAction = $this->cacheAction;
690: $out = $cache->cache($___viewFn, $out, $cached);
691: }
692: }
693: return $out;
694: }
695: 696: 697: 698: 699: 700: 701: 702:
703: function &_loadHelpers(&$loaded, $helpers, $parent = null) {
704: if (empty($loaded)) {
705: $helpers[] = 'Session';
706: }
707:
708: foreach ($helpers as $i => $helper) {
709: $options = array();
710:
711: if (!is_int($i)) {
712: $options = $helper;
713: $helper = $i;
714: }
715: $plugin = $this->plugin;
716:
717: if (strpos($helper, '.') !== false) {
718: list($plugin, $helper) = explode('.', $helper);
719: }
720: $helperCn = $helper . 'Helper';
721:
722: if (!isset($loaded[$helper])) {
723: if (!class_exists($helperCn)) {
724: $isLoaded = false;
725: if (!is_null($plugin)) {
726: $isLoaded = App::import('Helper', $plugin . '.' . $helper);
727: }
728: if (!$isLoaded) {
729: if (!App::import('Helper', $helper)) {
730: $this->cakeError('missingHelperFile', array(array(
731: 'helper' => $helper,
732: 'file' => Inflector::underscore($helper) . '.php',
733: 'base' => $this->base
734: )));
735: return false;
736: }
737: }
738: if (!class_exists($helperCn)) {
739: $this->cakeError('missingHelperClass', array(array(
740: 'helper' => $helper,
741: 'file' => Inflector::underscore($helper) . '.php',
742: 'base' => $this->base
743: )));
744: return false;
745: }
746: }
747: $loaded[$helper] =& new $helperCn($options);
748: $vars = array(
749: 'base', 'webroot', 'here', 'params', 'action', 'data', 'themeWeb', 'plugin'
750: );
751: $c = count($vars);
752:
753: for ($j = 0; $j < $c; $j++) {
754: $loaded[$helper]->{$vars[$j]} = $this->{$vars[$j]};
755: }
756:
757: if (!empty($this->validationErrors)) {
758: $loaded[$helper]->validationErrors = $this->validationErrors;
759: }
760: if (is_array($loaded[$helper]->helpers) && !empty($loaded[$helper]->helpers)) {
761: $loaded =& $this->_loadHelpers($loaded, $loaded[$helper]->helpers, $helper);
762: }
763: }
764: if (isset($loaded[$parent])) {
765: $loaded[$parent]->{$helper} =& $loaded[$helper];
766: }
767: }
768: return $loaded;
769: }
770: 771: 772: 773: 774: 775: 776: 777: 778:
779: function _getViewFileName($name = null) {
780: $subDir = null;
781:
782: if (!is_null($this->subDir)) {
783: $subDir = $this->subDir . DS;
784: }
785:
786: if ($name === null) {
787: $name = $this->action;
788: }
789: $name = str_replace('/', DS, $name);
790:
791: if (strpos($name, DS) === false && $name[0] !== '.') {
792: $name = $this->viewPath . DS . $subDir . Inflector::underscore($name);
793: } elseif (strpos($name, DS) !== false) {
794: if ($name{0} === DS || $name{1} === ':') {
795: if (is_file($name)) {
796: return $name;
797: }
798: $name = trim($name, DS);
799: } else if ($name[0] === '.') {
800: $name = substr($name, 3);
801: } else {
802: $name = $this->viewPath . DS . $subDir . $name;
803: }
804: }
805:
806: $paths = $this->_paths(Inflector::underscore($this->plugin));
807:
808: $exts = array($this->ext, '.ctp', '.thtml');
809: foreach ($exts as $ext) {
810: foreach ($paths as $path) {
811: if (file_exists($path . $name . $ext)) {
812: return $path . $name . $ext;
813: }
814: }
815: }
816: $defaultPath = $paths[0];
817:
818: if ($this->plugin) {
819: $pluginPaths = Configure::read('pluginPaths');
820: foreach ($paths as $path) {
821: if (strpos($path, $pluginPaths[0]) === 0) {
822: $defaultPath = $path;
823: break;
824: }
825: }
826: }
827: return $this->_missingView($defaultPath . $name . $this->ext, 'missingView');
828: }
829:
830: 831: 832: 833: 834: 835:
836: function _getLayoutFileName($name = null) {
837: if ($name === null) {
838: $name = $this->layout;
839: }
840: $subDir = null;
841:
842: if (!is_null($this->layoutPath)) {
843: $subDir = $this->layoutPath . DS;
844: }
845: $paths = $this->_paths(Inflector::underscore($this->plugin));
846: $file = 'layouts' . DS . $subDir . $name;
847:
848: $exts = array($this->ext, '.ctp', '.thtml');
849: foreach ($exts as $ext) {
850: foreach ($paths as $path) {
851: if (file_exists($path . $file . $ext)) {
852: return $path . $file . $ext;
853: }
854: }
855: }
856: return $this->_missingView($paths[0] . $file . $this->ext, 'missingLayout');
857: }
858: 859: 860: 861: 862: 863:
864: function _missingView($file, $error = 'missingView') {
865:
866: if ($error === 'missingView') {
867: $this->cakeError('missingView', array(
868: 'className' => $this->name,
869: 'action' => $this->action,
870: 'file' => $file,
871: 'base' => $this->base
872: ));
873: return false;
874: } elseif ($error === 'missingLayout') {
875: $this->cakeError('missingLayout', array(
876: 'layout' => $this->layout,
877: 'file' => $file,
878: 'base' => $this->base
879: ));
880: return false;
881: }
882: }
883: 884: 885: 886: 887: 888: 889:
890: function _paths($plugin = null, $cached = true) {
891: if ($plugin === null && $cached === true && !empty($this->__paths)) {
892: return $this->__paths;
893: }
894: $paths = array();
895: $viewPaths = Configure::read('viewPaths');
896: $corePaths = array_flip(Configure::corePaths('view'));
897:
898: if (!empty($plugin)) {
899: $count = count($viewPaths);
900: for ($i = 0; $i < $count; $i++) {
901: if (!isset($corePaths[$viewPaths[$i]])) {
902: $paths[] = $viewPaths[$i] . 'plugins' . DS . $plugin . DS;
903: }
904: }
905: $pluginPaths = Configure::read('pluginPaths');
906: $count = count($pluginPaths);
907:
908: for ($i = 0; $i < $count; $i++) {
909: $paths[] = $pluginPaths[$i] . $plugin . DS . 'views' . DS;
910: }
911: }
912: $paths = array_merge($paths, $viewPaths);
913:
914: if (empty($this->__paths)) {
915: $this->__paths = $paths;
916: }
917: return $paths;
918: }
919: 920: 921: 922:
923: function renderElement($name, $params = array(), $loadHelpers = false) {
924: return $this->element($name, $params, $loadHelpers);
925: }
926: }
927:
928: ?>
929: