Why changing the root template doesn't work
Both
Mage_Cms_IndexController::indexAction()
and
Mage_Cms_IndexController::viewAction()
which are responsible for displaying the default homepage and a CMS page respectively call a helper:
Mage::helper('cms/page')->renderPage($this, $pageId)
If you jump into the helper (located at app/code/core/Mage/Cms/Helper/Page.php) and follow renderPage()
to the protected method _renderPage()
you'll see that Magento is checking two times for a root template (Magento CE 1.7.0.2):
if ($page->getRootTemplate()) {
$handle = ($page->getCustomRootTemplate()
&& $page->getCustomRootTemplate() != 'empty'
&& $inRange) ? $page->getCustomRootTemplate() : $page->getRootTemplate();
$action->getLayout()->helper('page/layout')->applyHandle($handle);
}
and
if ($page->getRootTemplate()) {
$action->getLayout()->helper('page/layout')
->applyTemplate($page->getRootTemplate());
}
Both calls happen after layout handles like "cms_page" and the like are processed, so you are out of luck here.
What you can do to change the root template
There is an event cms_page_render
which you can use for adding your own XML layout handle on CMS pages. Create your own extension (I'll spare some details here) and configure the event observer in your config.xml
:
<?xml version="1.0"?>
<config>
<modules>
<Emzee_Cms>
<version>0.0.1</version>
</Emzee_Cms>
</modules>
<global>
<events>
<cms_page_render>
<observers>
<emzee_cms_page_render>
<class>emzee_cms/observer</class>
<method>cms_page_render</method>
</emzee_cms_page_render>
</observers>
</cms_page_render>
</events>
<models>
<emzee_cms>
<class>Emzee_Cms_Model</class>
</emzee_cms>
</models>
</global>
</config>
Add your event observer:
<?php
class Emzee_Cms_Model_Observer
{
public function cms_page_render(Varien_Event_Observer $observer)
{
$action = $observer->getEvent()->getControllerAction();
$actionName = strtolower($action->getFullActionName());
$action->getLayout()->getUpdate()
->addHandle($actionName . '_after');
return $this;
}
}
Finally, add your new layout XML handle (e.g. in your local.xml
):
<?xml version="1.0"?>
<layout version="0.1.0">
<cms_index_index_after>
<reference name="root">
<action method="setTemplate"><template>page/1column.phtml</template></action>
</reference>
</cms_index_index_after>
</layout>
You can use this method as well to add a cms_page_view_after
handle or create page specific handles as cms_page_render
passes the $page
object to your observer.
Why you can't add a block to the 'reference left'
Are you sure that the template you're using has a left column? This question may sound silly but the default "2 columns with right bar" layout for example only offers a 'content' and a 'right' area. I can add blocks to the right column using cms_page
without problems so this could be the problem.
In general, you can only easily add blocks to references and echo them if
- the chosen root template uses the block you're referencing (see
app/design/frontend/base/default/template/page/*.phtml
) and
- the block you're referencing either is of type
core/text_list
, calls $this->getChildhtml()
without arguments or does something else to echo all child blocks.
Without further details, I can't tell you why your blocks are not echo'd in the left or right column.
Once the layout is loaded (as it is for the controller_action_layout_generate_blocks_after
event) all of the actions have been processed. Adding a new handle to the update object at this point (or even manually merging its contents into the already-merged contents) doesn't make sense as you would have to regenerate things. You should take a different approach.
Stop and think what you need to do: unset JS on the head block, an instance of Mage_Page_Block_Html_Head
.
If you leave your current observer as it is configured, you can do
public function removePopupJs($observer)
{
$list = $this->getRemoveList(); // not sure how you want to transport values in
$root = $observer->getLayout()->getBlock('root');
$head = $observer->getLayout()->getBlock('head');
if (
is_object($root)
&& $root->getTemplate() == 'page/popup.phtml'
&& is_object($head)
)
{
foreach ($list as $type => $path) {
$head->removeItem($type,$path);
}
}
}
As I said, not sure how you want to propagate "remove this, not that" into the observer context. It would suck to maintain dual lists in separate "spaces", i.e. one in layout XML for adding and one in PHP for removing. Perhaps, if all JS needing removal is added in one layout XML file, you could re-load()
that file, or you could tag each JS-add directive with a custom attribute to select via XPATH.
You could also get rid of the observer and do the above in a helper method which just needs to be called after all JS has been added to the head block. Add your action to both the <customer_logged_out />
and <customer_logged_in />
handles as they will be executed on all page loads and after most other handles are executed.
There are other options as well. Layout it wicked flexible.
Best Answer
You can't do that. Specifically, you can't use the
<update/>
tag in a CMS page's Layout Update XML field.Magento processes the
<update/>
nodes when it is initially loading the layout update files, and layout updates from thecore_layout_update
table. This is done with thefetchRecursiveUpdates
function.A CMS page, however, is rendered later on via a helper method. The rendering of a CMS page includes this code
The
loadLayoutUpdates
call is the standard loading of the layout, including thefetchRecursiveUpdates
mentioned above.Then, Magento looks for a custom layout XML update set on the CMS page object
and adds it directly to the list of updates to apply
This means the scanning for the
update
tag is never done.If I was trying to implement something like this, I'd add a new block in the CMS page's layout update XML, and then add the shared layout items programmatically in the blocks
_prepareLayout
method. (not sure off the top of my head if that would work w/r/t to timing, but that'd be where I'd start)