PageRenderTime 54ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/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
Possible License(s): CC-BY-SA-3.0, LGPL-2.1, GPL-2.0, WTFPL
  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. * Layout model
  28. *
  29. * @category Mage
  30. * @package Mage_Core
  31. */
  32. class Mage_Core_Model_Layout extends Varien_Simplexml_Config
  33. {
  34. /**
  35. * Layout Update module
  36. *
  37. * @var Mage_Core_Model_Layout_Update
  38. */
  39. protected $_update;
  40. /**
  41. * Blocks registry
  42. *
  43. * @var array
  44. */
  45. protected $_blocks = array();
  46. /**
  47. * Cache of block callbacks to output during rendering
  48. *
  49. * @var array
  50. */
  51. protected $_output = array();
  52. /**
  53. * Layout area (f.e. admin, frontend)
  54. *
  55. * @var string
  56. */
  57. protected $_area;
  58. /**
  59. * Helper blocks cache for this layout
  60. *
  61. * @var array
  62. */
  63. protected $_helpers = array();
  64. /**
  65. * Flag to have blocks' output go directly to browser as oppose to return result
  66. *
  67. * @var boolean
  68. */
  69. protected $_directOutput = false;
  70. /**
  71. * Class constructor
  72. *
  73. * @param array $data
  74. */
  75. public function __construct($data=array())
  76. {
  77. $this->_elementClass = Mage::getConfig()->getModelClassName('core/layout_element');
  78. $this->setXml(simplexml_load_string('<layout/>', $this->_elementClass));
  79. $this->_update = Mage::getModel('core/layout_update');
  80. parent::__construct($data);
  81. }
  82. /**
  83. * Layout update instance
  84. *
  85. * @return Mage_Core_Model_Layout_Update
  86. */
  87. public function getUpdate()
  88. {
  89. return $this->_update;
  90. }
  91. /**
  92. * Set layout area
  93. *
  94. * @param string $area
  95. * @return Mage_Core_Model_Layout
  96. */
  97. public function setArea($area)
  98. {
  99. $this->_area = $area;
  100. return $this;
  101. }
  102. /**
  103. * Retrieve layout area
  104. *
  105. * @return string
  106. */
  107. public function getArea()
  108. {
  109. return $this->_area;
  110. }
  111. /**
  112. * Declaring layout direct output flag
  113. *
  114. * @param bool $flag
  115. * @return Mage_Core_Model_Layout
  116. */
  117. public function setDirectOutput($flag)
  118. {
  119. $this->_directOutput = $flag;
  120. return $this;
  121. }
  122. /**
  123. * Retrieve derect output flag
  124. *
  125. * @return bool
  126. */
  127. public function getDirectOutput()
  128. {
  129. return $this->_directOutput;
  130. }
  131. /**
  132. * Loyout xml generation
  133. *
  134. * @return Mage_Core_Model_Layout
  135. */
  136. public function generateXml()
  137. {
  138. $xml = $this->getUpdate()->asSimplexml();
  139. $removeInstructions = $xml->xpath("//remove");
  140. if (is_array($removeInstructions)) {
  141. foreach ($removeInstructions as $infoNode) {
  142. $attributes = $infoNode->attributes();
  143. $blockName = (string)$attributes->name;
  144. if ($blockName) {
  145. $ignoreNodes = $xml->xpath("//block[@name='".$blockName."']");
  146. if (!is_array($ignoreNodes)) {
  147. continue;
  148. }
  149. $ignoreReferences = $xml->xpath("//reference[@name='".$blockName."']");
  150. if (is_array($ignoreReferences)) {
  151. $ignoreNodes = array_merge($ignoreNodes, $ignoreReferences);
  152. }
  153. foreach ($ignoreNodes as $block) {
  154. if ($block->getAttribute('ignore') !== null) {
  155. continue;
  156. }
  157. if (($acl = (string)$attributes->acl) && Mage::getSingleton('admin/session')->isAllowed($acl)) {
  158. continue;
  159. }
  160. if (!isset($block->attributes()->ignore)) {
  161. $block->addAttribute('ignore', true);
  162. }
  163. }
  164. }
  165. }
  166. }
  167. $this->setXml($xml);
  168. return $this;
  169. }
  170. /**
  171. * Create layout blocks hierarchy from layout xml configuration
  172. *
  173. * @param Mage_Core_Layout_Element|null $parent
  174. */
  175. public function generateBlocks($parent=null)
  176. {
  177. if (empty($parent)) {
  178. $parent = $this->getNode();
  179. }
  180. foreach ($parent as $node) {
  181. $attributes = $node->attributes();
  182. if ((bool)$attributes->ignore) {
  183. continue;
  184. }
  185. switch ($node->getName()) {
  186. case 'block':
  187. $this->_generateBlock($node, $parent);
  188. $this->generateBlocks($node);
  189. break;
  190. case 'reference':
  191. $this->generateBlocks($node);
  192. break;
  193. case 'action':
  194. $this->_generateAction($node, $parent);
  195. break;
  196. }
  197. }
  198. }
  199. /**
  200. * Add block object to layout based on xml node data
  201. *
  202. * @param Varien_Simplexml_Element $node
  203. * @param Varien_Simplexml_Element $parent
  204. * @return Mage_Core_Model_Layout
  205. */
  206. protected function _generateBlock($node, $parent)
  207. {
  208. if (!empty($node['class'])) {
  209. $className = (string)$node['class'];
  210. } else {
  211. $className = (string)$node['type'];
  212. }
  213. $blockName = (string)$node['name'];
  214. $_profilerKey = 'BLOCK: '.$blockName;
  215. Varien_Profiler::start($_profilerKey);
  216. $block = $this->addBlock($className, $blockName);
  217. if (!$block) {
  218. return $this;
  219. }
  220. if (!empty($node['parent'])) {
  221. $parentBlock = $this->getBlock((string)$node['parent']);
  222. } else {
  223. $parentName = $parent->getBlockName();
  224. if (!empty($parentName)) {
  225. $parentBlock = $this->getBlock($parentName);
  226. }
  227. }
  228. if (!empty($parentBlock)) {
  229. $alias = isset($node['as']) ? (string)$node['as'] : '';
  230. if (isset($node['before'])) {
  231. $sibling = (string)$node['before'];
  232. if ('-'===$sibling) {
  233. $sibling = '';
  234. }
  235. $parentBlock->insert($block, $sibling, false, $alias);
  236. } elseif (isset($node['after'])) {
  237. $sibling = (string)$node['after'];
  238. if ('-'===$sibling) {
  239. $sibling = '';
  240. }
  241. $parentBlock->insert($block, $sibling, true, $alias);
  242. } else {
  243. $parentBlock->append($block, $alias);
  244. }
  245. }
  246. if (!empty($node['template'])) {
  247. $block->setTemplate((string)$node['template']);
  248. }
  249. if (!empty($node['output'])) {
  250. $method = (string)$node['output'];
  251. $this->addOutputBlock($blockName, $method);
  252. }
  253. Varien_Profiler::stop($_profilerKey);
  254. return $this;
  255. }
  256. /**
  257. * Enter description here...
  258. *
  259. * @param Varien_Simplexml_Element $node
  260. * @param Varien_Simplexml_Element $parent
  261. * @return Mage_Core_Model_Layout
  262. */
  263. protected function _generateAction($node, $parent)
  264. {
  265. if (isset($node['ifconfig']) && ($configPath = (string)$node['ifconfig'])) {
  266. if (!Mage::getStoreConfigFlag($configPath)) {
  267. return $this;
  268. }
  269. }
  270. $method = (string)$node['method'];
  271. if (!empty($node['block'])) {
  272. $parentName = (string)$node['block'];
  273. } else {
  274. $parentName = $parent->getBlockName();
  275. }
  276. $_profilerKey = 'BLOCK ACTION: '.$parentName.' -> '.$method;
  277. Varien_Profiler::start($_profilerKey);
  278. if (!empty($parentName)) {
  279. $block = $this->getBlock($parentName);
  280. }
  281. if (!empty($block)) {
  282. $args = (array)$node->children();
  283. unset($args['@attributes']);
  284. foreach ($args as $key => $arg) {
  285. if (($arg instanceof Mage_Core_Model_Layout_Element)) {
  286. if (isset($arg['helper'])) {
  287. $helperName = explode('/', (string)$arg['helper']);
  288. $helperMethod = array_pop($helperName);
  289. $helperName = implode('/', $helperName);
  290. $arg = $arg->asArray();
  291. unset($arg['@']);
  292. $args[$key] = call_user_func_array(array(Mage::helper($helperName), $helperMethod), $arg);
  293. } else {
  294. /**
  295. * if there is no helper we hope that this is assoc array
  296. */
  297. $arr = array();
  298. foreach($arg as $subkey => $value) {
  299. $arr[(string)$subkey] = $value->asArray();
  300. }
  301. if (!empty($arr)) {
  302. $args[$key] = $arr;
  303. }
  304. }
  305. }
  306. }
  307. if (isset($node['json'])) {
  308. $json = explode(' ', (string)$node['json']);
  309. foreach ($json as $arg) {
  310. $args[$arg] = Mage::helper('core')->jsonDecode($args[$arg]);
  311. }
  312. }
  313. $this->_translateLayoutNode($node, $args);
  314. call_user_func_array(array($block, $method), $args);
  315. }
  316. Varien_Profiler::stop($_profilerKey);
  317. return $this;
  318. }
  319. /**
  320. * Translate layout node
  321. *
  322. * @param Varien_Simplexml_Element $node
  323. * @param array $args
  324. **/
  325. protected function _translateLayoutNode($node, &$args)
  326. {
  327. if (isset($node['translate'])) {
  328. $items = explode(' ', (string)$node['translate']);
  329. foreach ($items as $arg) {
  330. if (isset($node['module'])) {
  331. $args[$arg] = Mage::helper((string)$node['module'])->__($args[$arg]);
  332. }
  333. else {
  334. $args[$arg] = Mage::helper('core')->__($args[$arg]);
  335. }
  336. }
  337. }
  338. }
  339. /**
  340. * Save block in blocks registry
  341. *
  342. * @param string $name
  343. * @param Mage_Core_Model_Layout $block
  344. */
  345. public function setBlock($name, $block)
  346. {
  347. $this->_blocks[$name] = $block;
  348. return $this;
  349. }
  350. /**
  351. * Remove block from registry
  352. *
  353. * @param string $name
  354. */
  355. public function unsetBlock($name)
  356. {
  357. $this->_blocks[$name] = null;
  358. unset($this->_blocks[$name]);
  359. return $this;
  360. }
  361. /**
  362. * Block Factory
  363. *
  364. * @param string $type
  365. * @param string $blockName
  366. * @param array $attributes
  367. * @return Mage_Core_Block_Abstract
  368. */
  369. public function createBlock($type, $name='', array $attributes = array())
  370. {
  371. try {
  372. $block = $this->_getBlockInstance($type, $attributes);
  373. } catch (Exception $e) {
  374. Mage::logException($e);
  375. return false;
  376. }
  377. if (empty($name) || '.'===$name{0}) {
  378. $block->setIsAnonymous(true);
  379. if (!empty($name)) {
  380. $block->setAnonSuffix(substr($name, 1));
  381. }
  382. $name = 'ANONYMOUS_'.sizeof($this->_blocks);
  383. } elseif (isset($this->_blocks[$name]) && Mage::getIsDeveloperMode()) {
  384. //Mage::throwException(Mage::helper('core')->__('Block with name "%s" already exists', $name));
  385. }
  386. $block->setType($type);
  387. $block->setNameInLayout($name);
  388. $block->addData($attributes);
  389. $block->setLayout($this);
  390. $this->_blocks[$name] = $block;
  391. Mage::dispatchEvent('core_layout_block_create_after', array('block'=>$block));
  392. return $this->_blocks[$name];
  393. }
  394. /**
  395. * Add a block to registry, create new object if needed
  396. *
  397. * @param string|Mage_Core_Block_Abstract $blockClass
  398. * @param string $blockName
  399. * @return Mage_Core_Block_Abstract
  400. */
  401. public function addBlock($block, $blockName)
  402. {
  403. return $this->createBlock($block, $blockName);
  404. }
  405. /**
  406. * Create block object instance based on block type
  407. *
  408. * @param string $block
  409. * @param array $attributes
  410. * @return Mage_Core_Block_Abstract
  411. */
  412. protected function _getBlockInstance($block, array $attributes=array())
  413. {
  414. if (is_string($block)) {
  415. if (strpos($block, '/')!==false) {
  416. if (!$block = Mage::getConfig()->getBlockClassName($block)) {
  417. Mage::throwException(Mage::helper('core')->__('Invalid block type: %s', $block));
  418. }
  419. }
  420. if (class_exists($block, false) || mageFindClassFile($block)) {
  421. $block = new $block($attributes);
  422. }
  423. }
  424. if (!$block instanceof Mage_Core_Block_Abstract) {
  425. Mage::throwException(Mage::helper('core')->__('Invalid block type: %s', $block));
  426. }
  427. return $block;
  428. }
  429. /**
  430. * Retrieve all blocks from registry as array
  431. *
  432. * @return array
  433. */
  434. public function getAllBlocks()
  435. {
  436. return $this->_blocks;
  437. }
  438. /**
  439. * Get block object by name
  440. *
  441. * @param string $name
  442. * @return Mage_Core_Block_Abstract
  443. */
  444. public function getBlock($name)
  445. {
  446. if (isset($this->_blocks[$name])) {
  447. return $this->_blocks[$name];
  448. } else {
  449. return false;
  450. }
  451. }
  452. /**
  453. * Add a block to output
  454. *
  455. * @param string $blockName
  456. * @param string $method
  457. */
  458. public function addOutputBlock($blockName, $method='toHtml')
  459. {
  460. //$this->_output[] = array($blockName, $method);
  461. $this->_output[$blockName] = array($blockName, $method);
  462. return $this;
  463. }
  464. public function removeOutputBlock($blockName)
  465. {
  466. unset($this->_output[$blockName]);
  467. return $this;
  468. }
  469. /**
  470. * Get all blocks marked for output
  471. *
  472. * @return string
  473. */
  474. public function getOutput()
  475. {
  476. $out = '';
  477. if (!empty($this->_output)) {
  478. foreach ($this->_output as $callback) {
  479. $out .= $this->getBlock($callback[0])->$callback[1]();
  480. }
  481. }
  482. return $out;
  483. }
  484. /**
  485. * Retrieve messages block
  486. *
  487. * @return Mage_Core_Block_Messages
  488. */
  489. public function getMessagesBlock()
  490. {
  491. $block = $this->getBlock('messages');
  492. if ($block) {
  493. return $block;
  494. }
  495. return $this->createBlock('core/messages', 'messages');
  496. }
  497. /**
  498. * Enter description here...
  499. *
  500. * @param string $type
  501. * @return Mage_Core_Helper_Abstract
  502. */
  503. public function getBlockSingleton($type)
  504. {
  505. if (!isset($this->_helpers[$type])) {
  506. $className = Mage::getConfig()->getBlockClassName($type);
  507. if (!$className) {
  508. Mage::throwException(Mage::helper('core')->__('Invalid block type: %s', $type));
  509. }
  510. $helper = new $className();
  511. if ($helper) {
  512. if ($helper instanceof Mage_Core_Block_Abstract) {
  513. $helper->setLayout($this);
  514. }
  515. $this->_helpers[$type] = $helper;
  516. }
  517. }
  518. return $this->_helpers[$type];
  519. }
  520. /**
  521. * Retrieve helper object
  522. *
  523. * @param string $name
  524. * @return Mage_Core_Helper_Abstract
  525. */
  526. public function helper($name)
  527. {
  528. $helper = Mage::helper($name);
  529. if (!$helper) {
  530. return false;
  531. }
  532. return $helper->setLayout($this);
  533. }
  534. /**
  535. * Lookup module name for translation from current specified layout node
  536. *
  537. * Priorities:
  538. * 1) "module" attribute in the element
  539. * 2) "module" attribute in any ancestor element
  540. * 3) layout handle name - first 1 or 2 parts (namespace is determined automatically)
  541. *
  542. * @param Varien_Simplexml_Element $node
  543. * @return string
  544. */
  545. public static function findTranslationModuleName(Varien_Simplexml_Element $node)
  546. {
  547. $result = $node->getAttribute('module');
  548. if ($result) {
  549. return (string)$result;
  550. }
  551. foreach (array_reverse($node->xpath('ancestor::*[@module]')) as $element) {
  552. $result = $element->getAttribute('module');
  553. if ($result) {
  554. return (string)$result;
  555. }
  556. }
  557. foreach ($node->xpath('ancestor-or-self::*[last()-1]') as $handle) {
  558. $name = Mage::getConfig()->determineOmittedNamespace($handle->getName());
  559. if ($name) {
  560. return $name;
  561. }
  562. }
  563. return 'core';
  564. }
  565. }