Magento: template based on attribute set - templates

I want to create different product views based on the attribute set the product belongs to:
does Magento provide a way to do this?
- UPDATE -
Following dan.codes suggestion I've added
$update->addHandle('PRODUCT_ATTRIBUTE_SET_ID_'.$product->getAttributeSetId());
to Mage_Catalog_ProductController (I duplicated ProductController.php and put it in local/Mage/Catalog/controllers/).
Then I added this to catalog.xml
<PRODUCT_ATTRIBUTE_SET_ID_9> // PRODUCT ID of Book Attribute Set
<label>Catalog Product View (Book)</label>
<reference name="product.info">
<block type="catalog/product_view_type_book" name="product.info.book" as="product_type_data" template="catalog/product/view/attribute_set/book.phtml">
<block type="core/text_list" name="product.info.book.extra" as="product_type_data_extra"/>
</block>
</reference>
</PRODUCT_ATTRIBUTE_SET_ID_9>
just after
<PRODUCT_TYPE_virtual translate="label" module="catalog">
<label>Catalog Product View (Virtual)</label>
<reference name="product.info">
<block type="catalog/product_view_type_virtual" name="product.info.virtual" as="product_type_data" template="catalog/product/view/type/virtual.phtml">
<block type="core/text_list" name="product.info.virtual.extra" as="product_type_data_extra"/>
</block>
</reference>
</PRODUCT_TYPE_virtual>
I then created catalog/product/view/attribute_set/book.phtml, but it is not displayed in my product view page.
- UPDATE MAGENTO 1.5 -
I've noticed that the handler update has moved in the last Magento release.
$update->addHandle('PRODUCT_TYPE_'.$product->getTypeId());
$update->addHandle('PRODUCT_'.$product->getId());
is in Mage/Catalog/Helper/Product/View.php now.
I've tested and it still works great!

No it doesn't but you can extend the functionality to do so by extending the _initProductLayout method in Mage_Catalog_ProductController under where the code is this
$update = $this->getLayout()->getUpdate();
$update->addHandle('default');
$this->addActionLayoutHandles();
$update->addHandle('PRODUCT_TYPE_'.$product->getTypeId());
$update->addHandle('PRODUCT_'.$product->getId());
You could add
$update->addHandle('PRODUCT_ATTRIBUTE_SET_ID_'.$product->getAttributeSetId());
Then in your layout.xml you could have
<PRODUCT_ATTRIBUTE_SET_ID_IDHERE>
<reference name="root">
<action method="setTemplate"><template>template/path/here.html</template></action>
</reference>
</PRODUCT_ATTRIBUTE_SET_ID_IDHERE>

If in case you want to switch view.phtml based on the attribute set then here is what you need to do:
<PRODUCT_ATTRIBUTE_SET_ID_9>
<label>Catalog Product View (Default)</label>
<reference name="product.info">
<action method="setTemplate"><template>catalog/product/custom-view.phtml</template></action>
</reference>
</PRODUCT_ATTRIBUTE_SET_ID_9>
Just add this in your catalog.xml or local.xml
Hope this helps.
Thanks

There is a good tutorial on this: http://magebase.com/magento-tutorials/creating-custom-layout-handles/
This uses following event: controller_action_layout_load_before
For this I set up in config.xml following
<events>
<controller_action_layout_load_before>
<observers>
<mymodule>
<class>mymodule/observer</class>
<method>addAttributeSetHandle</method>
</mymodule>
</observers>
</controller_action_layout_load_before>
</events>
And in Observer.php I will have
public function addAttributeSetHandle(Varien_Event_Observer $observer)
{
$product = Mage::registry('current_product');
/**
* Return if it is not product page
*/
if (!$this->isBookProduct($product)) {
return;
}
$niceName = 'book';
/* #var $update Mage_Core_Model_Layout_Update */
$update = $observer
->getEvent()
->getLayout()
->getUpdate();
$handles = $update->getHandles(); // Store all handles in a variable
$update->resetHandles(); // Remove all handles
/**
* Rearrange layout handles to ensure PRODUCT_<product_id>
* handle is added last
*/
foreach ($handles as $handle) {
$update->addHandle($handle);
if ($handle == 'PRODUCT_TYPE_' . $product->getTypeId()) {
$update->addHandle('PRODUCT_ATTRIBUTE_SET_' . $niceName);
}
}
}
protected function isBookProduct($product)
{
if (null === $product || !($product instanceof Mage_Catalog_Model_Product)) {
return false;
}
// TODO instead of hardcoded value we could use here something neat to get by name thru eav/entity_attribute_set model, some config value which hold that ID or use some other approach...
$book_set_id = 9;
if ($product->getAttributeSetId() != $book_set_id) {
return false;
}
return true;
}
This makes possibility to use in layout xml following:
<?xml version="1.0"?>
<layout version="0.1.0">
<PRODUCT_ATTRIBUTE_SET_book>
<reference name="product.info">
<action method="setTemplate">
<template>mymodule/book/product/view.phtml</template>
</action>
</reference>
</PRODUCT_ATTRIBUTE_SET_book>
</layout>

Related

Create Sitecore Item from Existing template

I am new to sitecore, when I try to create new item for existing template. I am unable to call my master database.Need some guide for how to create new item in sitecore and how to show it on my running sitecore page? Just need some hint regarding flow of after creation how to render it on existing sitecore page.
<configuration type="Sitecore.Search.SearchConfiguration, Sitecore.Kernel" singleInstance="true">
<indexes hint="list:AddIndex">
<index id="system" type="Sitecore.Search.Index, Sitecore.Kernel">
<param desc="name">$(id)</param>
<param desc="folder">__system</param>
<Analyzer ref="search/analyzer" />
<locations hint="list:AddCrawler">
<core type="Sitecore.Search.Crawlers.DatabaseCrawler,Sitecore.Kernel">
<Database>core</Database>
<Root>/sitecore/content</Root>
<include hint="list:IncludeTemplate">
<application>{EB06CEC0-5E2D-4DC4-875B-01ADCC577D13}</application>
</include>
<Tags>application</Tags>
<Boost>2.0</Boost>
</core>
<core-controlpanel type="Sitecore.Search.Crawlers.DatabaseCrawler,Sitecore.Kernel">
<Database>core</Database>
<Root>/sitecore/content/applications/control panel</Root>
<include hint="list:IncludeTemplate">
<taskoption>{BDB6FA46-2F76-4BDE-8138-52B56C2FC47E}</taskoption>
</include>
<Tags>taskoption</Tags>
<Boost>1.9</Boost>
</core-controlpanel>
<master type="Sitecore.Search.Crawlers.DatabaseCrawler, Sitecore.Kernel">
<Database>master</Database>
<Tags>master content</Tags>
</master>
</locations>
</index>
</indexes>
</configuration>
I will write the code I usually execute:
Database database = Database.GetDatabase("master");
Item rooItem = database.GetItem(new ID(parentItemId));
if (rootItem == null) return null;
TemplateID pageTemplateId = new TemplateID(new ID(templateId));
string validName = ItemUtil.ProposeValidItemName(name);
using (new Sitecore.SecurityModel.SecurityDisabler())
{
Item newItem = rootItem.Add(validName, pageTemplateId);
if (newItem == null) return null;
//TODO: publish item
}
rootItem could be the home item
templateId will be the id of your page template (remember this page should have layout and renderings defined)

How to add custom words behind product price

I am trying to figure out how to add "Per Square Feet" behind the Product Price. It should be only applied to selected products - not to all. Any help is appreciated.
This is the screenshot for reference
Thank you for the photo it helped a lot. I suggest adding this into vqmod/vqcache. For more information on VQmod look here: https://code.google.com/p/vqmod/. If not you can just take the HTML, PHP, and Query code and put it directly in your core files.
View:
<file name="catalog/view/theme/*/template/product/product.tpl"> // where it will insert the below code
<operation>
<search position="before"><![CDATA[<?php if ($tax) { ?>]]></search> // this places your code before the code in the CDATA[....what ever is here....]
<add><![CDATA[ // the below code is what you are adding
<?php if isset($pricePerSqFt) { ?>
<p><?php echo $pricePerSqFt; ?></p>]]></add>
</operation>
</file>
Controller:
$this->load->model('catalog/product');
$this->data['pricePerSqFt'] = $this->model_catalog_product->getData();
Model:
<file name="catalog/model/catalog/product.php">
<operation>
<search position="after"><![CDATA[class ModelCatalogProduct extends Model {]]></search>
<add>
<![CDATA[
public function getData() {
$query = $this->db->query("SELECT what_data_you_want FROM table_name"); // will grab the data from database
if (isset($query)) {
return $query;
}else{
return NULL;
}
}
]]>
</add>
</operation>
</file>

Get item based on displayname in sitecore

I need to get a specific item based on it's displayname, how do I do that?
For example I want to get this item /sitecore/content/mysite/about
But on the site is translated to multiple languages and could be something like www.site.com/om (in Sitecore it would be /sitecore/content/mysite/om)
There are a couple approaches you can take. The most efficent is to leverage the Content Search API, but the challenge is that Display Name is excluded from the index by default. A simple patch file can fix this:
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<contentSearch>
<indexConfigurations>
<defaultLuceneIndexConfiguration>
<exclude>
<__display_name>
<patch:delete />
</__display_name>
</exclude>
</defaultLuceneIndexConfiguration>
</indexConfigurations>
</contentSearch>
</sitecore>
</configuration>
Then your code would be something like:
public Item GetItemByDisplayName(string displayName)
{
var searchIndex = ContentSearchManager.GetIndex("sitecore_master_index"); // sub your index name
using (var context = searchIndex.CreateSearchContext())
{
var searchResultItems =
context.GetQueryable<SearchResultItem>().Where(i => i["Display Name"].Equals(displayName)).FirstOrDefault();
return searchResultItems == null ? null : searchResultItems.GetItem();
}
}
This all is assuming you are on Sitecore 7. If you're on Sitecore 6, your option are limited and are probably not going to perform well if you content tree is large. Nonetheless, your function might look like:
public Item GetItemByDisplayName(string displayName)
{
var query = string.Format("fast:/sitecore/content//*[#__Display Name='{0}']", displayName);
var item = Sitecore.Context.Database.SelectSingleItem(query);
return item;
}
Please note that this will be horribly inefficent since under the covers this is basically walking the content tree.
Often, you wouldn't need to. LinkManager (responsible for generating all your item URLs) has an option to base the URLs on Display Name instead of Item.Name.
var d = LinkManager.GetDefaultUrlOptions();
d.UseDisplayName = true;
This can also be configured in configuration. Find and amend this section in your Web.config (or patch it via include files):
<linkManager defaultProvider="sitecore">
<providers>
<clear />
<add name="sitecore" type="Sitecore.Links.LinkProvider, Sitecore.Kernel"
addAspxExtension="false" alwaysIncludeServerUrl="false" encodeNames="true"
languageEmbedding="never" languageLocation="filePath" lowercaseUrls="true"
shortenUrls="true" useDisplayName="false" />
</providers>
</linkManager>
To truly do exactly what you ask is a quite involved process. If you point DotPeek to Sitecore.Pipelines.HttpRequest.ItemResolver, you can step through the ResolveUsingDisplayName() method. It essentially loops through child items, comparing the URL Part to the "__Display Name" field. You would have to cook up something similar.

Opening GDK Glassware through Mirror API Glassware MenuItem

I have problem integrating GDK Glassware and Mirror API Glassware as described here. I need to open GDK glassware application using Mirroe api Glassware app MenuItem. Can I send data bundle with intent. Does anybody have an idea about that.
Thank you.
I have finally figured out a way to do that
First add your custom scheme to android activity tag in AndroidManifest.xml
<activity
android:name="com.sanath.MainActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<action android:name="com.google.android.glass.action.VOICE_TRIGGER" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<data android:scheme="com.sanath.scheme" />
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<meta-data android:name="com.google.android.glass.VoiceTrigger"
android:resource="#xml/vision_voice_trigger" />
</activity>
Then in Glassware timeline MenuItem add like following
new MenuItem(){
Action = "OPEN_URI",
Payload = "com.sanath.scheme://open/Welcome/2014",
Values = new MenuValue[]
{
new MenuValue()
{
DisplayName = "Open",
State = "DEFAULT"
},
new MenuValue()
{
DisplayName = "Launching",
State = "PENDING"
},
new MenuValue()
{
DisplayName = "Launched",
State = "CONFIRMED"
},
},
},
}
Then inside your Activity OnCreate method you can get data as following
Uri data = getIntent().getData();
List params = data.getPathSegments();
String param0 = params.get(0); // "welcome"
String param1 = params.get(1); //"2014"
String welcomeMsg = param0+" to "+param1;
/*show time line card
* */
Card welcomeCard =new Card(this);
welcomeCard.setText(welcomeMsg);
welcomeCard.setFootnote(param1);
View view =welcomeCard.toView();
setContentView(view);
Hope this will help others
It is not possible to provide data through bundle, but you can use query parameters or hash fragment in your URI to provide the necessary data.
Example:
myscheme://<SOME_PATH>?param1=value1&param2&value2
Then, in your GDK Glassware, simply parse the query parameters and process their values.

How to do proper nesting in a Magento theme?

I've copied the default Magento theme in the two dirs and renamed it.
After a lot of tweaking I've come to a point where I'm stuck.
I've got a 3columns.phtml file where the basic layout resides:
top (top.phtml)
header (header.phtml)
main (main.phtml)
footer (footer.phtml)
with main.phtml:
left.phtml
middle.phtml
right.phtml
Now when I referenced these blocks separately in the 3columns.phtml with the getChildHtml() function everything was peachy. Now, after putting them in the main.phtml file it doesn't seem to work anymore. Any content in the main.phtml is not shown (not even simpel html tags). Thinking it could have to do with the xml layout files I looked into page.xml but found out some things are handled by catalog.xml
I now have these block declarations in page.xml:
top (no template= set)
header (no template= set)
main (template="page/html/main.phtml")
left (template="page/html/left.phtml")
middle (template="page/html/middle.phtml")
topmenu (template="page/html/topmenu.phtml")
breadcrumbs (no template= set)
right (template="page/html/right.phtml")
footer (template="page/html/footer.phtml")
And eventhough top and header have no template set their html shows up fine in the frontend. the footer shows up fine too.
Left's output is shown, but not wrapped by main's output.
Right's output the same.
Middle's output shows, but any getChildHtml calls in it don't.
Any ideas as to what's going on?
Also, how do the page.xml and catalog.xml relate to the template? Does every single call to getChildHtml have to be in the page.xml file? what's the difference between page and catalog.
The part of page.xml: http://www.snipplr.com/view/66919/part-of-pagexml-not-working/
The part of catalog.xml: http://www.snipplr.com/view/66920/default-part-of-catalogxml-not-working/
The 3columns.phtml: http://www.snipplr.com/view/66921/main-theme-file-3columnsphtml/
For the detail explanation you can read Alanstorm's book, "No Frills Magento Layout".
I'll give you "brief" explanation so that you have a little enlightment.
In our controller, when we call $this->loadLayout();, it will automatically "call" <default> handle.
Later, all of the handle in all layout xml will be merged.
Let's say you have <default> in page.xml, then you also have <default> in your catalog.xml.
After the call of $this->loadLayout() those <default> from page.xml and catalog.xml will be merged.
That's the relation between them.
We can say <default> is the main of everything. It is the main structure. Any other layout usually "referencing" from it.
Let's say you want to add something in the main body (content), we will call:
<reference name="content">
<block type="core/template" name="testing" template="some/test/template.phtml"></block>
</reference>
Those tag means: Put <block type="core/template" name="testing" template="some/test/template.phtml"></block> under block with name content.
If you see the definition of block with name content:
<block type="core/text_list" name="content" as="content" translate="label">
<label>Main Content Area</label>
</block>
You can see the type is core/text_list. Block with this type will render every child under it and it DOES NOT need a template.
As you can see, there is no definition for template="blabla/blibli.phtml".
The type of block that have template is core/block_template. In this type of block, if you want to render its child, you have to "consciously" call echo $this->getChildHtml('mykid').
Do you get it?
That's why your definition is incorrect: <block type="core/text_list" name="main" as="main" translate="label" template="page/html/main.phtml">.
core/text_list doesn't need and doesn't have a template. You can imagine if "content" is a core/block_template: every time you're going to create a child under "content" you have to "consciously" call echo $this->getChildHtml('mykid'). Let's say you have 100 child, you will need to call echo $this->getChildHtml('mykid1'), echo $this->getChildHtml('mykid2'), ..., echo $this->getChildHtml('mykid100').
Then let's see your catalog.xml:
<default>
<!-- Mage_Catalog -->
<block type="core/template" name="top" template="page/html/top.phtml"></block>
<block type="core/template" name="header" template="page/html/header.phtml"></block>
<block type="core/template" name="main" template="page/html/main.phtml">
<block type="core/template" name="left" template="page/html/left.phtml"></block>
<block type="core/template" name="middle" template="page/html/middle.phtml"></block>
<block type="core/template" name="right" template="page/html/right.phtml"></block>
</block>
<reference name="footer_links">
<action method="addLink" translate="label title" module="catalog" ifconfig="catalog/seo/site_map">
<label>Site Map</label>
<url helper="catalog/map/getCategoryUrl" />
<title>Site Map</title>
</action>
</reference>
<block type="catalog/product_price_template" name="catalog_product_price_template" />
</default>
I don't get it what you're going to achieve from this catalog.xml. There are no reference for: <block type="core/template" name="top" template="page/html/top.phtml"></block>, <block type="core/template" name="header" template="page/html/header.phtml"></block>, etc.
If you're going to add some layout (children), you need to "referencing" from an existing one. Let's say you're going to add children under block with name "main" and you're going to put the code in your catalog.xml:
Then your catalog.xml will be like this:
<default>
<reference name="main">
<block type="core/template" name="i" template="my/new/template1.phtml"></block>
<block type="core/template" name="dont" template="my/new/template2.phtml"></block>
<block type="core/template" name="know" template="my/new/template3.phtml"></block>
</reference>
</default>
Now it depends on the definition of block with name "main".
If your block with name "main" is:
<block type="core/text_list" name="main" as="main" translate="label">
...
</block>
then those blocks (blocks with name "i", "dont", "know") will be automatically called (rendered).
If your block with name "main" is:
<block type="core/template" name="main" as="main" translate="label" template="page/html/main.phtml">
...
</block>
Then you need to call this in your page/html/main.phtml:
echo $this->getChildHtml('i');
echo $this->getChildHtml('dont');
echo $this->getChildHtml('know');
That's as far as I can explain as it's pretty much incorrect things in your layout, if you get the point, you can fix it by reading my explanation.
Few tips:
To see which handles loaded, put this code in your controller under $this->loadLayout(); :
$layout = Mage::getSingleton('core/layout');
$updates = $layout->getUpdate();
$handles = $updates->getHandles();
var_dump($handles);
To see the generated xml from load layout, put this code in your controller under $this->loadLayout(); :
$layout = Mage::getSingleton('core/layout');
$updates = $layout->getUpdate();
$xml = $updates->asString();
var_dump($xml);
Maybe you will need echo "<pre>" / wrap using htmlentities / Mage::log for neat looking purpose.
Here are some possible explanations.
Since left, middle, and right are children of 'main' you need to call getChildChildHtml (note the double child).
Since 'main' is a child (nested) inside 3columns it is not considered it's own page type (i.e. 1 column), the better solution seems to edit the 3columns.phtml file to have the blocks that you want.
When calling getChildHtml the parameter passed to it is the name='' attribute in the xml (so for example if the 'right' block needs to be called, then we need to find the name attribute in the xml - in this case it's 'right' and use that for the parameter).
The left, footer etc. blocks are showing because they are already being called in the 3columns.phtml file.
Also double check that the template file to 'main' is in the right location.
I hope these were some helpful pointers