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:
28: 29: 30: 31: 32: 33:
34: class Mage_Core_Model_Layout extends Varien_Simplexml_Config
35: {
36:
37: 38: 39: 40: 41:
42: protected $_update;
43:
44: 45: 46: 47: 48:
49: protected $_blocks = array();
50:
51: 52: 53: 54: 55:
56: protected $_output = array();
57:
58: 59: 60: 61: 62:
63: protected $_area;
64:
65: 66: 67: 68: 69:
70: protected $_helpers = array();
71:
72: 73: 74: 75: 76:
77: protected $_directOutput = false;
78:
79: 80: 81: 82: 83:
84: public function __construct($data=array())
85: {
86: $this->_elementClass = Mage::getConfig()->getModelClassName('core/layout_element');
87: $this->setXml(simplexml_load_string('<layout/>', $this->_elementClass));
88: $this->_update = Mage::getModel('core/layout_update');
89: parent::__construct($data);
90: }
91:
92: 93: 94: 95: 96:
97: public function getUpdate()
98: {
99: return $this->_update;
100: }
101:
102: 103: 104: 105: 106: 107:
108: public function setArea($area)
109: {
110: $this->_area = $area;
111: return $this;
112: }
113:
114: 115: 116: 117: 118:
119: public function getArea()
120: {
121: return $this->_area;
122: }
123:
124: 125: 126: 127: 128: 129:
130: public function setDirectOutput($flag)
131: {
132: $this->_directOutput = $flag;
133: return $this;
134: }
135:
136: 137: 138: 139: 140:
141: public function getDirectOutput()
142: {
143: return $this->_directOutput;
144: }
145:
146: 147: 148: 149: 150:
151: public function generateXml()
152: {
153: $xml = $this->getUpdate()->asSimplexml();
154: $removeInstructions = $xml->xpath("//remove");
155: if (is_array($removeInstructions)) {
156: foreach ($removeInstructions as $infoNode) {
157: $attributes = $infoNode->attributes();
158: $blockName = (string)$attributes->name;
159: if ($blockName) {
160: $ignoreNodes = $xml->xpath("//block[@name='".$blockName."']");
161: if (!is_array($ignoreNodes)) {
162: continue;
163: }
164: $ignoreReferences = $xml->xpath("//reference[@name='".$blockName."']");
165: if (is_array($ignoreReferences)) {
166: $ignoreNodes = array_merge($ignoreNodes, $ignoreReferences);
167: }
168:
169: foreach ($ignoreNodes as $block) {
170: if ($block->getAttribute('ignore') !== null) {
171: continue;
172: }
173: $acl = (string)$attributes->acl;
174: if ($acl && Mage::getSingleton('admin/session')->isAllowed($acl)) {
175: continue;
176: }
177: if (!isset($block->attributes()->ignore)) {
178: $block->addAttribute('ignore', true);
179: }
180: }
181: }
182: }
183: }
184: $this->setXml($xml);
185: return $this;
186: }
187:
188: 189: 190: 191: 192:
193: public function generateBlocks($parent=null)
194: {
195: if (empty($parent)) {
196: $parent = $this->getNode();
197: }
198: foreach ($parent as $node) {
199: $attributes = $node->attributes();
200: if ((bool)$attributes->ignore) {
201: continue;
202: }
203: switch ($node->getName()) {
204: case 'block':
205: $this->_generateBlock($node, $parent);
206: $this->generateBlocks($node);
207: break;
208:
209: case 'reference':
210: $this->generateBlocks($node);
211: break;
212:
213: case 'action':
214: $this->_generateAction($node, $parent);
215: break;
216: }
217: }
218: }
219:
220: 221: 222: 223: 224: 225: 226:
227: protected function _generateBlock($node, $parent)
228: {
229: if (!empty($node['class'])) {
230: $className = (string)$node['class'];
231: } else {
232: $className = (string)$node['type'];
233: }
234:
235: $blockName = (string)$node['name'];
236: $_profilerKey = 'BLOCK: '.$blockName;
237: Varien_Profiler::start($_profilerKey);
238:
239: $block = $this->addBlock($className, $blockName);
240: if (!$block) {
241: return $this;
242: }
243:
244: if (!empty($node['parent'])) {
245: $parentBlock = $this->getBlock((string)$node['parent']);
246: } else {
247: $parentName = $parent->getBlockName();
248: if (!empty($parentName)) {
249: $parentBlock = $this->getBlock($parentName);
250: }
251: }
252: if (!empty($parentBlock)) {
253: $alias = isset($node['as']) ? (string)$node['as'] : '';
254: if (isset($node['before'])) {
255: $sibling = (string)$node['before'];
256: if ('-'===$sibling) {
257: $sibling = '';
258: }
259: $parentBlock->insert($block, $sibling, false, $alias);
260: } elseif (isset($node['after'])) {
261: $sibling = (string)$node['after'];
262: if ('-'===$sibling) {
263: $sibling = '';
264: }
265: $parentBlock->insert($block, $sibling, true, $alias);
266: } else {
267: $parentBlock->append($block, $alias);
268: }
269: }
270: if (!empty($node['template'])) {
271: $block->setTemplate((string)$node['template']);
272: }
273:
274: if (!empty($node['output'])) {
275: $method = (string)$node['output'];
276: $this->addOutputBlock($blockName, $method);
277: }
278: Varien_Profiler::stop($_profilerKey);
279:
280: return $this;
281: }
282:
283: 284: 285: 286: 287: 288: 289:
290: protected function _generateAction($node, $parent)
291: {
292: if (isset($node['ifconfig']) && ($configPath = (string)$node['ifconfig'])) {
293: if (!Mage::getStoreConfigFlag($configPath)) {
294: return $this;
295: }
296: }
297:
298: $method = (string)$node['method'];
299: if (!empty($node['block'])) {
300: $parentName = (string)$node['block'];
301: } else {
302: $parentName = $parent->getBlockName();
303: }
304:
305: $_profilerKey = 'BLOCK ACTION: '.$parentName.' -> '.$method;
306: Varien_Profiler::start($_profilerKey);
307:
308: if (!empty($parentName)) {
309: $block = $this->getBlock($parentName);
310: }
311: if (!empty($block)) {
312:
313: $args = (array)$node->children();
314: unset($args['@attributes']);
315:
316: foreach ($args as $key => $arg) {
317: if (($arg instanceof Mage_Core_Model_Layout_Element)) {
318: if (isset($arg['helper'])) {
319: $helperName = explode('/', (string)$arg['helper']);
320: $helperMethod = array_pop($helperName);
321: $helperName = implode('/', $helperName);
322: $arg = $arg->asArray();
323: unset($arg['@']);
324: $args[$key] = call_user_func_array(array(Mage::helper($helperName), $helperMethod), $arg);
325: } else {
326: 327: 328:
329: $arr = array();
330: foreach($arg as $subkey => $value) {
331: $arr[(string)$subkey] = $value->asArray();
332: }
333: if (!empty($arr)) {
334: $args[$key] = $arr;
335: }
336: }
337: }
338: }
339:
340: if (isset($node['json'])) {
341: $json = explode(' ', (string)$node['json']);
342: foreach ($json as $arg) {
343: $args[$arg] = Mage::helper('core')->jsonDecode($args[$arg]);
344: }
345: }
346:
347: $this->_translateLayoutNode($node, $args);
348: call_user_func_array(array($block, $method), $args);
349: }
350:
351: Varien_Profiler::stop($_profilerKey);
352:
353: return $this;
354: }
355:
356: 357: 358: 359: 360: 361:
362: protected function _translateLayoutNode($node, &$args)
363: {
364: if (isset($node['translate'])) {
365:
366: $moduleName = (isset($node['module'])) ? (string)$node['module'] : 'core';
367:
368:
369: $translatableArguments = explode(' ', (string)$node['translate']);
370: foreach ($translatableArguments as $translatableArgumentName) {
371: 372: 373: 374: 375:
376:
377: $argumentHierarchy = explode('.', $translatableArgumentName);
378: $argumentStack = &$args;
379: $canTranslate = true;
380: while (is_array($argumentStack) && count($argumentStack) > 0) {
381: $argumentName = array_shift($argumentHierarchy);
382: if (isset($argumentStack[$argumentName])) {
383: 384: 385: 386:
387: $argumentStack = &$argumentStack[$argumentName];
388: } else {
389:
390: $canTranslate = false;
391: break;
392: }
393: }
394: if ($canTranslate && is_string($argumentStack)) {
395:
396: $argumentStack = Mage::helper($moduleName)->__($argumentStack);
397: }
398: }
399: }
400: }
401:
402: 403: 404: 405: 406: 407:
408: public function setBlock($name, $block)
409: {
410: $this->_blocks[$name] = $block;
411: return $this;
412: }
413:
414: 415: 416: 417: 418:
419: public function unsetBlock($name)
420: {
421: $this->_blocks[$name] = null;
422: unset($this->_blocks[$name]);
423: return $this;
424: }
425:
426: 427: 428: 429: 430: 431: 432: 433:
434: public function createBlock($type, $name='', array $attributes = array())
435: {
436: try {
437: $block = $this->_getBlockInstance($type, $attributes);
438: } catch (Exception $e) {
439: Mage::logException($e);
440: return false;
441: }
442:
443: if (empty($name) || '.'===$name{0}) {
444: $block->setIsAnonymous(true);
445: if (!empty($name)) {
446: $block->setAnonSuffix(substr($name, 1));
447: }
448: $name = 'ANONYMOUS_'.sizeof($this->_blocks);
449: } elseif (isset($this->_blocks[$name]) && Mage::getIsDeveloperMode()) {
450:
451: }
452:
453: $block->setType($type);
454: $block->setNameInLayout($name);
455: $block->addData($attributes);
456: $block->setLayout($this);
457:
458: $this->_blocks[$name] = $block;
459: Mage::dispatchEvent('core_layout_block_create_after', array('block'=>$block));
460: return $this->_blocks[$name];
461: }
462:
463: 464: 465: 466: 467: 468: 469:
470: public function addBlock($block, $blockName)
471: {
472: return $this->createBlock($block, $blockName);
473: }
474:
475: 476: 477: 478: 479: 480: 481:
482: protected function _getBlockInstance($block, array $attributes=array())
483: {
484: if (is_string($block)) {
485: if (strpos($block, '/')!==false) {
486: if (!$block = Mage::getConfig()->getBlockClassName($block)) {
487: Mage::throwException(Mage::helper('core')->__('Invalid block type: %s', $block));
488: }
489: }
490: if (class_exists($block, false) || mageFindClassFile($block)) {
491: $block = new $block($attributes);
492: }
493: }
494: if (!$block instanceof Mage_Core_Block_Abstract) {
495: Mage::throwException(Mage::helper('core')->__('Invalid block type: %s', $block));
496: }
497: return $block;
498: }
499:
500:
501: 502: 503: 504: 505:
506: public function getAllBlocks()
507: {
508: return $this->_blocks;
509: }
510:
511: 512: 513: 514: 515: 516:
517: public function getBlock($name)
518: {
519: if (isset($this->_blocks[$name])) {
520: return $this->_blocks[$name];
521: } else {
522: return false;
523: }
524: }
525:
526: 527: 528: 529: 530: 531:
532: public function addOutputBlock($blockName, $method='toHtml')
533: {
534:
535: $this->_output[$blockName] = array($blockName, $method);
536: return $this;
537: }
538:
539: public function removeOutputBlock($blockName)
540: {
541: unset($this->_output[$blockName]);
542: return $this;
543: }
544:
545: 546: 547: 548: 549:
550: public function getOutput()
551: {
552: $out = '';
553: if (!empty($this->_output)) {
554: foreach ($this->_output as $callback) {
555: $out .= $this->getBlock($callback[0])->$callback[1]();
556: }
557: }
558:
559: return $out;
560: }
561:
562: 563: 564: 565: 566:
567: public function getMessagesBlock()
568: {
569: $block = $this->getBlock('messages');
570: if ($block) {
571: return $block;
572: }
573: return $this->createBlock('core/messages', 'messages');
574: }
575:
576: 577: 578: 579: 580: 581:
582: public function getBlockSingleton($type)
583: {
584: if (!isset($this->_helpers[$type])) {
585: $className = Mage::getConfig()->getBlockClassName($type);
586: if (!$className) {
587: Mage::throwException(Mage::helper('core')->__('Invalid block type: %s', $type));
588: }
589:
590: $helper = new $className();
591: if ($helper) {
592: if ($helper instanceof Mage_Core_Block_Abstract) {
593: $helper->setLayout($this);
594: }
595: $this->_helpers[$type] = $helper;
596: }
597: }
598: return $this->_helpers[$type];
599: }
600:
601: 602: 603: 604: 605: 606:
607: public function helper($name)
608: {
609: $helper = Mage::helper($name);
610: if (!$helper) {
611: return false;
612: }
613: return $helper->setLayout($this);
614: }
615:
616: 617: 618: 619: 620: 621: 622: 623: 624: 625: 626:
627: public static function findTranslationModuleName(Varien_Simplexml_Element $node)
628: {
629: $result = $node->getAttribute('module');
630: if ($result) {
631: return (string)$result;
632: }
633: foreach (array_reverse($node->xpath('ancestor::*[@module]')) as $element) {
634: $result = $element->getAttribute('module');
635: if ($result) {
636: return (string)$result;
637: }
638: }
639: foreach ($node->xpath('ancestor-or-self::*[last()-1]') as $handle) {
640: $name = Mage::getConfig()->determineOmittedNamespace($handle->getName());
641: if ($name) {
642: return $name;
643: }
644: }
645: return 'core';
646: }
647: }
648: