PageRenderTime 444ms CodeModel.GetById 80ms app.highlight 248ms RepoModel.GetById 53ms app.codeStats 1ms

/app/code/core/Mage/Core/Model/Layout.php

https://bitbucket.org/claudiu_marginean/magento-hg-mirror
PHP | 621 lines | 355 code | 56 blank | 210 comment | 61 complexity | aac1809a498a12b3871dc90e14c92ca7 MD5 | raw file
  1<?php
  2/**
  3 * Magento
  4 *
  5 * NOTICE OF LICENSE
  6 *
  7 * This source file is subject to the Open Software License (OSL 3.0)
  8 * that is bundled with this package in the file LICENSE.txt.
  9 * It is also available through the world-wide-web at this URL:
 10 * http://opensource.org/licenses/osl-3.0.php
 11 * If you did not receive a copy of the license and are unable to
 12 * obtain it through the world-wide-web, please send an email
 13 * to license@magentocommerce.com so we can send you a copy immediately.
 14 *
 15 * DISCLAIMER
 16 *
 17 * Do not edit or add to this file if you wish to upgrade Magento to newer
 18 * versions in the future. If you wish to customize Magento for your
 19 * needs please refer to http://www.magentocommerce.com for more information.
 20 *
 21 * @category    Mage
 22 * @package     Mage_Core
 23 * @copyright   Copyright (c) 2010 Magento Inc. (http://www.magentocommerce.com)
 24 * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
 25 */
 26
 27
 28/**
 29 * Layout model
 30 *
 31 * @category   Mage
 32 * @package    Mage_Core
 33 */
 34class Mage_Core_Model_Layout extends Varien_Simplexml_Config
 35{
 36
 37    /**
 38     * Layout Update module
 39     *
 40     * @var Mage_Core_Model_Layout_Update
 41     */
 42    protected $_update;
 43
 44    /**
 45     * Blocks registry
 46     *
 47     * @var array
 48     */
 49    protected $_blocks = array();
 50
 51    /**
 52     * Cache of block callbacks to output during rendering
 53     *
 54     * @var array
 55     */
 56    protected $_output = array();
 57
 58    /**
 59     * Layout area (f.e. admin, frontend)
 60     *
 61     * @var string
 62     */
 63    protected $_area;
 64
 65    /**
 66     * Helper blocks cache for this layout
 67     *
 68     * @var array
 69     */
 70    protected $_helpers = array();
 71
 72    /**
 73     * Flag to have blocks' output go directly to browser as oppose to return result
 74     *
 75     * @var boolean
 76     */
 77    protected $_directOutput = false;
 78
 79    /**
 80     * Class constructor
 81     *
 82     * @param array $data
 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     * Layout update instance
 94     *
 95     * @return Mage_Core_Model_Layout_Update
 96     */
 97    public function getUpdate()
 98    {
 99        return $this->_update;
100    }
101
102    /**
103     * Set layout area
104     *
105     * @param   string $area
106     * @return  Mage_Core_Model_Layout
107     */
108    public function setArea($area)
109    {
110        $this->_area = $area;
111        return $this;
112    }
113
114    /**
115     * Retrieve layout area
116     *
117     * @return string
118     */
119    public function getArea()
120    {
121        return $this->_area;
122    }
123
124    /**
125     * Declaring layout direct output flag
126     *
127     * @param   bool $flag
128     * @return  Mage_Core_Model_Layout
129     */
130    public function setDirectOutput($flag)
131    {
132        $this->_directOutput = $flag;
133        return $this;
134    }
135
136    /**
137     * Retrieve derect output flag
138     *
139     * @return bool
140     */
141    public function getDirectOutput()
142    {
143        return $this->_directOutput;
144    }
145
146    /**
147     * Loyout xml generation
148     *
149     * @return Mage_Core_Model_Layout
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                        if (($acl = (string)$attributes->acl) && Mage::getSingleton('admin/session')->isAllowed($acl)) {
174                            continue;
175                        }
176                        if (!isset($block->attributes()->ignore)) {
177                            $block->addAttribute('ignore', true);
178                        }
179                    }
180                }
181            }
182        }
183        $this->setXml($xml);
184        return $this;
185    }
186
187    /**
188     * Create layout blocks hierarchy from layout xml configuration
189     *
190     * @param Mage_Core_Layout_Element|null $parent
191     */
192    public function generateBlocks($parent=null)
193    {
194        if (empty($parent)) {
195            $parent = $this->getNode();
196        }
197        foreach ($parent as $node) {
198            $attributes = $node->attributes();
199            if ((bool)$attributes->ignore) {
200                continue;
201            }
202            switch ($node->getName()) {
203                case 'block':
204                    $this->_generateBlock($node, $parent);
205                    $this->generateBlocks($node);
206                    break;
207
208                case 'reference':
209                    $this->generateBlocks($node);
210                    break;
211
212                case 'action':
213                    $this->_generateAction($node, $parent);
214                    break;
215            }
216        }
217    }
218
219    /**
220     * Add block object to layout based on xml node data
221     *
222     * @param Varien_Simplexml_Element $node
223     * @param Varien_Simplexml_Element $parent
224     * @return Mage_Core_Model_Layout
225     */
226    protected function _generateBlock($node, $parent)
227    {
228        if (!empty($node['class'])) {
229            $className = (string)$node['class'];
230        } else {
231            $className = (string)$node['type'];
232        }
233
234        $blockName = (string)$node['name'];
235        $_profilerKey = 'BLOCK: '.$blockName;
236        Varien_Profiler::start($_profilerKey);
237
238        $block = $this->addBlock($className, $blockName);
239        if (!$block) {
240            return $this;
241        }
242
243        if (!empty($node['parent'])) {
244            $parentBlock = $this->getBlock((string)$node['parent']);
245        } else {
246            $parentName = $parent->getBlockName();
247            if (!empty($parentName)) {
248                $parentBlock = $this->getBlock($parentName);
249            }
250        }
251        if (!empty($parentBlock)) {
252            $alias = isset($node['as']) ? (string)$node['as'] : '';
253            if (isset($node['before'])) {
254                $sibling = (string)$node['before'];
255                if ('-'===$sibling) {
256                    $sibling = '';
257                }
258                $parentBlock->insert($block, $sibling, false, $alias);
259            } elseif (isset($node['after'])) {
260                $sibling = (string)$node['after'];
261                if ('-'===$sibling) {
262                    $sibling = '';
263                }
264                $parentBlock->insert($block, $sibling, true, $alias);
265            } else {
266                $parentBlock->append($block, $alias);
267            }
268        }
269        if (!empty($node['template'])) {
270            $block->setTemplate((string)$node['template']);
271        }
272
273        if (!empty($node['output'])) {
274            $method = (string)$node['output'];
275            $this->addOutputBlock($blockName, $method);
276        }
277        Varien_Profiler::stop($_profilerKey);
278
279        return $this;
280    }
281
282    /**
283     * Enter description here...
284     *
285     * @param Varien_Simplexml_Element $node
286     * @param Varien_Simplexml_Element $parent
287     * @return Mage_Core_Model_Layout
288     */
289    protected function _generateAction($node, $parent)
290    {
291        if (isset($node['ifconfig']) && ($configPath = (string)$node['ifconfig'])) {
292            if (!Mage::getStoreConfigFlag($configPath)) {
293                return $this;
294            }
295        }
296
297        $method = (string)$node['method'];
298        if (!empty($node['block'])) {
299            $parentName = (string)$node['block'];
300        } else {
301            $parentName = $parent->getBlockName();
302        }
303
304        $_profilerKey = 'BLOCK ACTION: '.$parentName.' -> '.$method;
305        Varien_Profiler::start($_profilerKey);
306
307        if (!empty($parentName)) {
308            $block = $this->getBlock($parentName);
309        }
310        if (!empty($block)) {
311
312            $args = (array)$node->children();
313            unset($args['@attributes']);
314
315            foreach ($args as $key => $arg) {
316                if (($arg instanceof Mage_Core_Model_Layout_Element)) {
317                    if (isset($arg['helper'])) {
318                        $helperName = explode('/', (string)$arg['helper']);
319                        $helperMethod = array_pop($helperName);
320                        $helperName = implode('/', $helperName);
321                        $arg = $arg->asArray();
322                        unset($arg['@']);
323                        $args[$key] = call_user_func_array(array(Mage::helper($helperName), $helperMethod), $arg);
324                    } else {
325                        /**
326                         * if there is no helper we hope that this is assoc array
327                         */
328                        $arr = array();
329                        foreach($arg as $subkey => $value) {
330                            $arr[(string)$subkey] = $value->asArray();
331                        }
332                        if (!empty($arr)) {
333                            $args[$key] = $arr;
334                        }
335                    }
336                }
337            }
338
339            if (isset($node['json'])) {
340                $json = explode(' ', (string)$node['json']);
341                foreach ($json as $arg) {
342                    $args[$arg] = Mage::helper('core')->jsonDecode($args[$arg]);
343                }
344            }
345
346            $this->_translateLayoutNode($node, $args);
347            call_user_func_array(array($block, $method), $args);
348        }
349
350        Varien_Profiler::stop($_profilerKey);
351
352        return $this;
353    }
354
355    /**
356     * Translate layout node
357     *
358     * @param Varien_Simplexml_Element $node
359     * @param array $args
360     **/
361    protected function _translateLayoutNode($node, &$args)
362    {
363        if (isset($node['translate'])) {
364            $items = explode(' ', (string)$node['translate']);
365            foreach ($items as $arg) {
366                if (isset($node['module'])) {
367                    $args[$arg] = Mage::helper((string)$node['module'])->__($args[$arg]);
368                }
369                else {
370                    $args[$arg] = Mage::helper('core')->__($args[$arg]);
371                }
372            }
373        }
374    }
375
376    /**
377     * Save block in blocks registry
378     *
379     * @param string $name
380     * @param Mage_Core_Model_Layout $block
381     */
382    public function setBlock($name, $block)
383    {
384        $this->_blocks[$name] = $block;
385        return $this;
386    }
387
388    /**
389     * Remove block from registry
390     *
391     * @param string $name
392     */
393    public function unsetBlock($name)
394    {
395        $this->_blocks[$name] = null;
396        unset($this->_blocks[$name]);
397        return $this;
398    }
399
400    /**
401     * Block Factory
402     *
403     * @param     string $type
404     * @param     string $blockName
405     * @param     array $attributes
406     * @return    Mage_Core_Block_Abstract
407     */
408    public function createBlock($type, $name='', array $attributes = array())
409    {
410        try {
411            $block = $this->_getBlockInstance($type, $attributes);
412        } catch (Exception $e) {
413            Mage::logException($e);
414            return false;
415        }
416
417        if (empty($name) || '.'===$name{0}) {
418            $block->setIsAnonymous(true);
419            if (!empty($name)) {
420                $block->setAnonSuffix(substr($name, 1));
421            }
422            $name = 'ANONYMOUS_'.sizeof($this->_blocks);
423        } elseif (isset($this->_blocks[$name]) && Mage::getIsDeveloperMode()) {
424            //Mage::throwException(Mage::helper('core')->__('Block with name "%s" already exists', $name));
425        }
426
427        $block->setType($type);
428        $block->setNameInLayout($name);
429        $block->addData($attributes);
430        $block->setLayout($this);
431
432        $this->_blocks[$name] = $block;
433        Mage::dispatchEvent('core_layout_block_create_after', array('block'=>$block));
434        return $this->_blocks[$name];
435    }
436
437    /**
438     * Add a block to registry, create new object if needed
439     *
440     * @param string|Mage_Core_Block_Abstract $blockClass
441     * @param string $blockName
442     * @return Mage_Core_Block_Abstract
443     */
444    public function addBlock($block, $blockName)
445    {
446        return $this->createBlock($block, $blockName);
447    }
448
449    /**
450     * Create block object instance based on block type
451     *
452     * @param string $block
453     * @param array $attributes
454     * @return Mage_Core_Block_Abstract
455     */
456    protected function _getBlockInstance($block, array $attributes=array())
457    {
458        if (is_string($block)) {
459            if (strpos($block, '/')!==false) {
460                if (!$block = Mage::getConfig()->getBlockClassName($block)) {
461                    Mage::throwException(Mage::helper('core')->__('Invalid block type: %s', $block));
462                }
463            }
464            if (class_exists($block, false) || mageFindClassFile($block)) {
465                $block = new $block($attributes);
466            }
467        }
468        if (!$block instanceof Mage_Core_Block_Abstract) {
469            Mage::throwException(Mage::helper('core')->__('Invalid block type: %s', $block));
470        }
471        return $block;
472    }
473
474
475    /**
476     * Retrieve all blocks from registry as array
477     *
478     * @return array
479     */
480    public function getAllBlocks()
481    {
482        return $this->_blocks;
483    }
484
485    /**
486     * Get block object by name
487     *
488     * @param string $name
489     * @return Mage_Core_Block_Abstract
490     */
491    public function getBlock($name)
492    {
493        if (isset($this->_blocks[$name])) {
494            return $this->_blocks[$name];
495        } else {
496            return false;
497        }
498    }
499
500    /**
501     * Add a block to output
502     *
503     * @param string $blockName
504     * @param string $method
505     */
506    public function addOutputBlock($blockName, $method='toHtml')
507    {
508        //$this->_output[] = array($blockName, $method);
509        $this->_output[$blockName] = array($blockName, $method);
510        return $this;
511    }
512
513    public function removeOutputBlock($blockName)
514    {
515        unset($this->_output[$blockName]);
516        return $this;
517    }
518
519    /**
520     * Get all blocks marked for output
521     *
522     * @return string
523     */
524    public function getOutput()
525    {
526        $out = '';
527        if (!empty($this->_output)) {
528            foreach ($this->_output as $callback) {
529                $out .= $this->getBlock($callback[0])->$callback[1]();
530            }
531        }
532
533        return $out;
534    }
535
536    /**
537     * Retrieve messages block
538     *
539     * @return Mage_Core_Block_Messages
540     */
541    public function getMessagesBlock()
542    {
543        $block = $this->getBlock('messages');
544        if ($block) {
545            return $block;
546        }
547        return $this->createBlock('core/messages', 'messages');
548    }
549
550    /**
551     * Enter description here...
552     *
553     * @param string $type
554     * @return Mage_Core_Helper_Abstract
555     */
556    public function getBlockSingleton($type)
557    {
558        if (!isset($this->_helpers[$type])) {
559            $className = Mage::getConfig()->getBlockClassName($type);
560            if (!$className) {
561                Mage::throwException(Mage::helper('core')->__('Invalid block type: %s', $type));
562            }
563
564            $helper = new $className();
565            if ($helper) {
566                if ($helper instanceof Mage_Core_Block_Abstract) {
567                    $helper->setLayout($this);
568                }
569                $this->_helpers[$type] = $helper;
570            }
571        }
572        return $this->_helpers[$type];
573    }
574
575    /**
576     * Retrieve helper object
577     *
578     * @param   string $name
579     * @return  Mage_Core_Helper_Abstract
580     */
581    public function helper($name)
582    {
583        $helper = Mage::helper($name);
584        if (!$helper) {
585            return false;
586        }
587        return $helper->setLayout($this);
588    }
589
590    /**
591     * Lookup module name for translation from current specified layout node
592     *
593     * Priorities:
594     * 1) "module" attribute in the element
595     * 2) "module" attribute in any ancestor element
596     * 3) layout handle name - first 1 or 2 parts (namespace is determined automatically)
597     *
598     * @param Varien_Simplexml_Element $node
599     * @return string
600     */
601    public static function findTranslationModuleName(Varien_Simplexml_Element $node)
602    {
603        $result = $node->getAttribute('module');
604        if ($result) {
605            return (string)$result;
606        }
607        foreach (array_reverse($node->xpath('ancestor::*[@module]')) as $element) {
608            $result = $element->getAttribute('module');
609            if ($result) {
610                return (string)$result;
611            }
612        }
613        foreach ($node->xpath('ancestor-or-self::*[last()-1]') as $handle) {
614            $name = Mage::getConfig()->determineOmittedNamespace($handle->getName());
615            if ($name) {
616                return $name;
617            }
618        }
619        return 'core';
620    }
621}