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: class Mage_Core_Model_Layout_Update
29: {
30: 31: 32:
33: const LAYOUT_GENERAL_CACHE_TAG = 'LAYOUT_GENERAL_CACHE_TAG';
34:
35: 36: 37: 38: 39:
40: protected $_elementClass;
41:
42: 43: 44:
45: protected $_packageLayout;
46:
47: 48: 49: 50: 51:
52: protected $_cacheId;
53:
54: 55: 56: 57: 58:
59: protected $_cachePrefix;
60:
61: 62: 63: 64: 65:
66: protected $_updates = array();
67:
68: 69: 70: 71: 72:
73: protected $_handles = array();
74:
75: 76: 77: 78: 79:
80: protected $_subst = array();
81:
82: public function __construct()
83: {
84: $subst = Mage::getConfig()->getPathVars();
85: foreach ($subst as $k=>$v) {
86: $this->_subst['from'][] = '{{'.$k.'}}';
87: $this->_subst['to'][] = $v;
88: }
89: }
90:
91: public function getElementClass()
92: {
93: if (!$this->_elementClass) {
94: $this->_elementClass = Mage::getConfig()->getModelClassName('core/layout_element');
95: }
96: return $this->_elementClass;
97: }
98:
99: public function resetUpdates()
100: {
101: $this->_updates = array();
102: return $this;
103: }
104:
105: public function addUpdate($update)
106: {
107: $this->_updates[] = $update;
108: return $this;
109: }
110:
111: public function asArray()
112: {
113: return $this->_updates;
114: }
115:
116: public function asString()
117: {
118: return implode('', $this->_updates);
119: }
120:
121: public function resetHandles()
122: {
123: $this->_handles = array();
124: return $this;
125: }
126:
127: public function addHandle($handle)
128: {
129: if (is_array($handle)) {
130: foreach ($handle as $h) {
131: $this->_handles[$h] = 1;
132: }
133: } else {
134: $this->_handles[$handle] = 1;
135: }
136: return $this;
137: }
138:
139: public function removeHandle($handle)
140: {
141: unset($this->_handles[$handle]);
142: return $this;
143: }
144:
145: public function getHandles()
146: {
147: return array_keys($this->_handles);
148: }
149:
150: 151: 152: 153: 154:
155: public function getCacheId()
156: {
157: if (!$this->_cacheId) {
158: $this->_cacheId = 'LAYOUT_'.Mage::app()->getStore()->getId().md5(join('__', $this->getHandles()));
159: }
160: return $this->_cacheId;
161: }
162:
163: 164: 165: 166: 167: 168:
169: public function setCacheId($cacheId)
170: {
171: $this->_cacheId = $cacheId;
172: return $this;
173: }
174:
175: public function loadCache()
176: {
177: if (!Mage::app()->useCache('layout')) {
178: return false;
179: }
180:
181: if (!$result = Mage::app()->loadCache($this->getCacheId())) {
182: return false;
183: }
184:
185: $this->addUpdate($result);
186:
187: return true;
188: }
189:
190: public function saveCache()
191: {
192: if (!Mage::app()->useCache('layout')) {
193: return false;
194: }
195: $str = $this->asString();
196: $tags = $this->getHandles();
197: $tags[] = self::LAYOUT_GENERAL_CACHE_TAG;
198: return Mage::app()->saveCache($str, $this->getCacheId(), $tags, null);
199: }
200:
201: 202: 203: 204: 205: 206:
207: public function load($handles=array())
208: {
209: if (is_string($handles)) {
210: $handles = array($handles);
211: } elseif (!is_array($handles)) {
212: throw Mage::exception('Mage_Core', Mage::helper('core')->__('Invalid layout update handle'));
213: }
214:
215: foreach ($handles as $handle) {
216: $this->addHandle($handle);
217: }
218:
219: if ($this->loadCache()) {
220: return $this;
221: }
222:
223: foreach ($this->getHandles() as $handle) {
224: $this->merge($handle);
225: }
226:
227: $this->saveCache();
228: return $this;
229: }
230:
231: public function asSimplexml()
232: {
233: $updates = trim($this->asString());
234: $updates = '<'.'?xml version="1.0"?'.'><layout>'.$updates.'</layout>';
235: return simplexml_load_string($updates, $this->getElementClass());
236: }
237:
238: 239: 240: 241: 242: 243:
244: public function merge($handle)
245: {
246: $packageUpdatesStatus = $this->fetchPackageLayoutUpdates($handle);
247: if (Mage::app()->isInstalled()) {
248: $this->fetchDbLayoutUpdates($handle);
249: }
250:
251:
252:
253:
254: return $this;
255: }
256:
257: public function fetchFileLayoutUpdates()
258: {
259: $storeId = Mage::app()->getStore()->getId();
260: $elementClass = $this->getElementClass();
261: $design = Mage::getSingleton('core/design_package');
262: $cacheKey = 'LAYOUT_' . $design->getArea() . '_STORE' . $storeId . '_' . $design->getPackageName() . '_'
263: . $design->getTheme('layout');
264:
265: $cacheTags = array(self::LAYOUT_GENERAL_CACHE_TAG);
266: if (Mage::app()->useCache('layout') && ($layoutStr = Mage::app()->loadCache($cacheKey))) {
267: $this->_packageLayout = simplexml_load_string($layoutStr, $elementClass);
268: }
269: if (empty($layoutStr)) {
270: $this->_packageLayout = $this->getFileLayoutUpdatesXml(
271: $design->getArea(),
272: $design->getPackageName(),
273: $design->getTheme('layout'),
274: $storeId
275: );
276: if (Mage::app()->useCache('layout')) {
277: Mage::app()->saveCache($this->_packageLayout->asXml(), $cacheKey, $cacheTags, null);
278: }
279: }
280:
281:
282:
283:
284:
285:
286:
287:
288:
289:
290:
291:
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:
335:
336:
337:
338:
339: return $this;
340: }
341:
342: public function fetchPackageLayoutUpdates($handle)
343: {
344: $_profilerKey = 'layout/package_update: '.$handle;
345: Varien_Profiler::start($_profilerKey);
346: if (empty($this->_packageLayout)) {
347: $this->fetchFileLayoutUpdates();
348: }
349: foreach ($this->_packageLayout->$handle as $updateXml) {
350:
351: $this->fetchRecursiveUpdates($updateXml);
352: $this->addUpdate($updateXml->innerXml());
353: }
354: Varien_Profiler::stop($_profilerKey);
355:
356: return true;
357: }
358:
359: public function fetchDbLayoutUpdates($handle)
360: {
361: $_profilerKey = 'layout/db_update: '.$handle;
362: Varien_Profiler::start($_profilerKey);
363: $updateStr = $this->_getUpdateString($handle);
364: if (!$updateStr) {
365: return false;
366: }
367: $updateStr = '<update_xml>' . $updateStr . '</update_xml>';
368: $updateStr = str_replace($this->_subst['from'], $this->_subst['to'], $updateStr);
369: $updateXml = simplexml_load_string($updateStr, $this->getElementClass());
370: $this->fetchRecursiveUpdates($updateXml);
371: $this->addUpdate($updateXml->innerXml());
372:
373: Varien_Profiler::stop($_profilerKey);
374: return true;
375: }
376:
377: 378: 379: 380: 381: 382:
383: protected function _getUpdateString($handle)
384: {
385: return Mage::getResourceModel('core/layout')->fetchUpdatesByHandle($handle);
386: }
387:
388: public function fetchRecursiveUpdates($updateXml)
389: {
390: foreach ($updateXml->children() as $child) {
391: if (strtolower($child->getName())=='update' && isset($child['handle'])) {
392: $this->merge((string)$child['handle']);
393:
394: $this->addHandle((string)$child['handle']);
395: }
396: }
397: return $this;
398: }
399:
400: 401: 402: 403: 404: 405: 406: 407: 408:
409: public function getFileLayoutUpdatesXml($area, $package, $theme, $storeId = null)
410: {
411: if (null === $storeId) {
412: $storeId = Mage::app()->getStore()->getId();
413: }
414:
415: $design = Mage::getSingleton('core/design_package');
416: $layoutXml = null;
417: $elementClass = $this->getElementClass();
418: $updatesRoot = Mage::app()->getConfig()->getNode($area.'/layout/updates');
419: Mage::dispatchEvent('core_layout_update_updates_get_after', array('updates' => $updatesRoot));
420: $updateFiles = array();
421: foreach ($updatesRoot->children() as $updateNode) {
422: if ($updateNode->file) {
423: $module = $updateNode->getAttribute('module');
424: if ($module && Mage::getStoreConfigFlag('advanced/modules_disable_output/' . $module, $storeId)) {
425: continue;
426: }
427: $updateFiles[] = (string)$updateNode->file;
428: }
429: }
430:
431: $updateFiles[] = 'local.xml';
432: $layoutStr = '';
433: foreach ($updateFiles as $file) {
434: $filename = $design->getLayoutFilename($file, array(
435: '_area' => $area,
436: '_package' => $package,
437: '_theme' => $theme
438: ));
439: if (!is_readable($filename)) {
440: continue;
441: }
442: $fileStr = file_get_contents($filename);
443: $fileStr = str_replace($this->_subst['from'], $this->_subst['to'], $fileStr);
444: $fileXml = simplexml_load_string($fileStr, $elementClass);
445: if (!$fileXml instanceof SimpleXMLElement) {
446: continue;
447: }
448: $layoutStr .= $fileXml->innerXml();
449: }
450: $layoutXml = simplexml_load_string('<layouts>'.$layoutStr.'</layouts>', $elementClass);
451: return $layoutXml;
452: }
453: }
454: