Tag: magento

  • Bxgy 0.1.2 release

    Bxgy 0.1.0 and Bxgy 0.1.1 packaged a layout file bxgy.xml to a wrong place. Bxgy 0.1.2 is a quick release not on schedule.

    Thanks to Carsten for pointing out the error. Sorry to John, Hamichok and Tsk for reporting the bug but I pointed them into wrong direction.

    Down BuyXGetY.tar.gz

  • Is appending block by handle possible in Magento layout xml?

    In the product list on Magento category page, I have a deeply nested blocks for each product in the list. The structure is something like:

    <block type="..." name="product_in_category" template="...">
     <block type="..." name="product_options_selector" template="...">
     <block type="..." name="product_price_container" template="...">
     <!-- possibly more opening tags of nestings here -->
     <block type="..." name="product_prices" template="..."/>
     <!-- closing tags of nestings -->
     </block>
     </block>
    </block>
    

    If I want to add all above blocks to product list, there is a ready solution:

    <my_product_list>
     <reference name="product_list">
     <block type="..." name="product_in_category" template="...">
     <block type="..." name="product_options_selector" template="...">
     <block type="..." name="product_price_container" template="...">
     <block type="..." name="product_prices" template="..."/>
     </block>
     </block>
     </block>
     </reference>
    </my_product_list>
    

    Once I put them together as a handle, I can easily duplicate them to 3 category layouts.

    <catalog_category_default>
     <update handle="my_product_list" />
    </catalog_category_default>
    
    <catalog_category_layered>
     <update handle="my_product_list" />
    </catalog_category_layered>
    
    <catalog_category_layered_nochildren>
     <update handle="my_product_list" />
    </catalog_category_layered_nochildren>
    

    So every time I make changes to product list, I only change the content in <my_product_list> and 3 category layouts get updates automatically.

    It saves me a lot of time. However, since product list is also used in search result and advanced search result, product list in the result misses updates because it is named as “search_result_list” instead of “product_list”. The instance reaction is duplicating codes for “search_result_list”, i.e.

    <my_product_list_of_result>
     <reference name="search_result_list">
     <block type="..." name="product_in_category" template="...">
     <block type="..." name="product_options_selector" template="...">
     <block type="..." name="product_price_container" template="...">
     <block type="..." name="product_prices" template="..."/>
     </block>
     </block>
     </block>
     </reference>
    </my_product_list_of_result>
    
    <catalogsearch_result_index>
     <update handle="my_product_list_of_result" />
    </catalogsearch_result_index>
    
    <catalogsearch_advanced_result>
     <update handle="my_product_list_of_result" />
    </catalogsearch_advanced_result>
    

    I do not like duplication, because I keep forgetting when and where the duplication is when I want to make changes. So after a think, I come up with an alternative:

    <every_product>
     <reference name="product_in_category">
     <block type="..." name="product_options_selector" template="...">
     <block type="..." name="product_price_container" template="...">
     <block type="..." name="product_prices" template="..."/>
     </block>
     </block>
     </reference>
    </every_product>
    
    <add_every_product_to_category>
     <reference name="product_list">
     <block type="..." name="product_in_category" template="..."/>
     </reference>
    </add_every_product_to_category>
    <!-- Cannot merge to the above handle! Update handle does not work in the same handle when block is newly appended. -->
    <add_every_product_to_category>
     <update handle="every_product" />
    </add_every_product_to_category>
    
    <add_every_product_to_result>
     <reference name="search_result_list">
     <block type="..." name="product_in_category" template="..."/>
     </reference>
    </add_every_product_to_result>
    <!-- Cannot merge to the above handle! Update handle does not work in the same handle when block is newly appended. -->
    <add_every_product_to_result>
     <update handle="every_product" />
    </add_every_product_to_result>
    
    <catalog_category_default>
     <update handle="add_every_product_to_category" />
    </catalog_category_default>
    
    <catalog_category_layered>
     <update handle="add_every_product_to_category" />
    </catalog_category_layered>
    
    <catalog_category_layered_nochildren>
     <update handle="add_every_product_to_category" />
    </catalog_category_layered_nochildren>
    
    <catalogsearch_result_index>
     <update handle="add_every_product_to_result" />
    </catalogsearch_result_index>
    
    <catalogsearch_advanced_result>
     <update handle="add_every_product_to_result" />
    </catalogsearch_advanced_result>
    

    You will see the code is messy. That is why I am not satisfied with the alternative, either. I would like the layout xml file recognise something like

    <my_product_layout>
     <block type="..." name="product_in_category" template="...">
     <block type="..." name="product_options_selector" template="...">
     <block type="..." name="product_price_container" template="...">
     <block type="..." name="product_prices" template="..."/>
     </block>
     </block>
     </block>
    </my_product_layout>
    
    <add_layout_to_category>
     <reference name="product_list">
     <layout handle="my_product_layout" />
     </reference>
    </add_layout_to_category>
    
    <add_layout_to_result>
     <reference name="search_result_list">
     <layout handle="my_product_layout" />
     </reference>
    </add_layout_to_result>
    
    <catalog_category_default>
     <update handle="add_layout_to_category" />
    </catalog_category_default>
    
    <catalog_category_layered>
     <update handle="add_layout_to_category" />
    </catalog_category_layered>
    
    <catalog_category_layered_nochildren>
     <update handle="add_layout_to_category" />
    </catalog_category_layered_nochildren>
    
    <catalogsearch_result_index>
     <update handle="add_layout_to_result" />
    </catalogsearch_result_index>
    
    <catalogsearch_advanced_result>
     <update handle="add_layout_to_result" />
    </catalogsearch_advanced_result>
    

    Handling the layout of product list is not the worst case. Handling the layout of order, invoice, shipment, credit note is much more time taking, becuase each document is rendered in many media, such as customer screen, admin screen, email, pdf. And if I have custom documents, and custom documents are customised for suppliers, drop shipper, etc, an easy layout update mechanism will be very handy.

    In a summary, I am trying to add child blocks by handle. At mement I am intended to believe there is no way to trigger the layout xml into batch adding blocks by handle without overriding Block abstract or Layout model, but there maybe some tricky way out there. If you know it, let me know please.

  • Create a Magento admin panel user with read only privilege

    A third party Adwords professional requested a read only access to Magento admin panel, which raised the question to me: how to create a Magento admin panel user with read only privilege?

    I am keen to do this job via ACL. Magento ACL role resources start from module to controller to action, I can not differentiate read/write privilege at the root level of role resources. It means it involves a lot of hassle coding up ACL in adminhtml.xml when creating a role with read access to all modules, because I have to code for every action, for example, indexAction is a read role resource, saveAction is a write role resource, editAction can be a write role resource if it actually saves data itself, or be a read role resource if post data are posted to saveAction.

    Luckily, in my case, the Adwords professional only requested access to two modules – Catalog and Reports. As for Reports module, I treat the whole module as read only, so I simple check “Reports” node in the role resources tree.

    As for Catalog module, the Adwords professional only requested to view product list, so I create several children of “Manage Products” node. The children map to all actions in Mage_Adminhtml_Catalog_ProductController, for example, product list maps to indexAction, product detail maps to editAction, product add and update maps to saveAction. I check indexAction node. I could check editAction (because it is read only process) as well but the Adwords professional did not see its necessity.

    The last thing left to do is differentiating privilege by role resources node. It is a very simple change – override Mage_Adminhtml_Catalog_ProductController’s _isAllowed() method from

    protected function _isAllowed()
    {
    	return Mage::getSingleton('admin/session')->isAllowed('catalog/products');
    }
    

    to

    protected function _isAllowed()
    {
    	$action = $this->getRequest()->getRequestedActionName();
    	return Mage::getSingleton('admin/session')->isAllowed('catalog/products/'.$action);
    }
    
    

    I think there may be another to differentiate read and write role resource using ACL, because when I look at a sample adminhtml.xml file, it says

    	<acl>
    		<resources>
    			<all>
    				<title>Allow Everything</title>
    			</all>
    		</resources>
    	</acl>
    

    What does it means? Does it imply I can do something like

    	<acl>
    		<resources>
    			<read>
    				<title>Read only</title>
    			</read>
    			<write>
    				<title>Read and write</title>
    			</write>
    		</resources>
    	</acl>
    

    ?

  • Lack of jQuery menu UI

    I started coding for jqol – thoughts from last week. But I ran into some dilemma.

    A very important UI of an eshop is the menu. However, I do not have a mature jQuery UI menu (jQuery menu) widget to use. jQuery menu widget is only available from jQuery UI 1.9, which is still in a stage of milestone 3. The most recent jQuery menu can only theme a flat menu. It can not theme nested (or so called hierarchy) menu. In short, it is useless.

    Luckily, Filament Group contributed a very sophisticated jQuery menu UI plugin (fg menu). It can theme flat or hierarchy menu into different styles – flat, flyout, ipod. However, when I try to use it, I find fg menu is based on a different concept to jQuery menu. Fg menu is simulating an OS start menu, and the object being menued is the start button, i.e. use the following code to initialise a menu.

    jQuery("button").menu({options}); //menu content inside options
    

    While jQuery menu is exactly a Magento menu, i.e. use the following code to initialise a menu.

    jQuery("#nav").menu({options}); //jQuery("#nav").html() itself is the menu content
    

    I am more comfortable taking jQuery core team’s logic, specially in the case of converting Magento menu to jQuery menu widget. Someone from jQuery team has clarified they are of different structures. The status quo is Magento core team’s menu is not completed, but fg menu is something I can use now. So my decision is –

    Stay with fg menu for now, and change my code when jQuery menu is mature with all the options I want.

  • Go to messages inbox back to work

    Magento 1.5.0.0 各测试版的发布很紧密,我有些应接不暇。新功能来不及去试,倒发现一个老功能不灵光了,就是 Magento admin panel 里显示来自官方的 notifications 的 “Go to messages index” 的链接地址不正确,点了仍是当前页面。

    因为我有个 fpcp 模块改动了一些 global messages 和 messages 的显示逻辑,是基于 Magento 1.4.2.0 开发的,一开始我还以为是我的改动与 1.5.0.0 不兼容。但我把 fpcp 整个 disable,”Go to messages index” 也同样不工作,这下我才想到是 Magento 1.5.0.0 bug。

    还没来得及 report bug to Magento,我又发现 Magento 1.5.0.0 rc1 版已经修正了这个 bug。去看了 release notes,果然提到 Fixed “Go to notifications” link works improperly。

    Go to messages inbox Latest Message: Magento Preview Version CE 1.5.0.0-rc1 is now available Read details
    Go to messages inbox Latest Message: Magento Preview Version CE 1.5.0.0-rc1 is now available Read details
  • What does backend type static mean in Magento?

    About a year ago, I noticed some category or product attributes, for example, sku, path, etc., were given backend type “static” in class Mage_Catalog_Model_Resource_Eav_Mysql4_Setup. But I failed to find out what static meant here, or I did not know what the difference was between static and datetime/decimal/int/text/varchar. All I knew at that time was if I gave my user defined attribute a “static” type, the attribute values were stored in _varchar table.

    Recently, I dived deeper into Magento EAV module. Now I can answer my question.

    An entity can have some “static” attributes, whose values are stored in entity main table. Take sku for example, sku values are stored in catalog_product_entity table. Although sku is varchar type, its values are not stored in catalog_product_entity_varchar. Magento does not lookup catalog_product_entity_varchar for sku values because in eav_attribute table, sku backend type is defined as “static”.

    It also explains why I can get sku values without add it to select, but I can not do the same with non static attributes.

    $collection = Mage::getModel('catalog/product')->getCollection();
    echo $collection->getFirstItem()->getSku();
    

    It will output the sku.

    $collection = Mage::getModel('catalog/product')->getCollection();
    echo $collection->getFirstItem()->getName();
    

    It will not output the name.

    $collection = Mage::getModel('catalog/product')->getCollection()->addAttributeToSelect('name');
    echo $collection->getFirstItem()->getName();
    

    Calling addAttributeToSelect is a must to retrieve non static attribute values.

    For user defined attributes of existing entity, if I do not change the structure of main entity table, even I specify backend as “static” in entity setup class, Magento has nowhere to store their values to but fallback to _varchar table.

  • Update layout in Magento against a specific route

    I got some complaints to my bxgy module about reminder message is not showing in shopping cart as it supposed to be. But I checked over and over on my machine and did not find anything preventing reminder message being shown.

    By a chance I realise maybe those people changed Magento interface and theme in a not nice way. What is a nice way? By my definition, any extension or change to Magento original code should make sure

    • do as few changes as possible
    • Magento default theme can always be fallen back to

    I take it as a basic requirement of customisation, but I can not guarantee everyone else see it through. If I rely on a module layout xml file to trigger an action against a specific route, I get a higher chance of my layout xml file being bypassed by some incompatible theme.

    In bxgy module version 0.1.0 to 0.1.1, I wrote a layout handle checkout_cart_index in bxgy.xml, which is expected to add a message to the message block against route checkout/cart/index. If bxgy.xml is bypassed (when the theme settings do not take default theme fallback into consideration), nothing will happen. Do I have to use layout xml file to trigger an action against a specific route? Absolutely not. There are many ways around.

    In a newer version of bxgy module, I use an event observer to trigger “controller_action_postdispatch_checkout_cart_index” event, and add message to the message block in this observer. Because adding reminder message is something more relevant to functionality than presentation, the code’s logic is better if it is moved from design folder to app folder. And event observer is independent of theme settings.

    BTW, I finished coding this new version of bxgy, but I want to put together some other modules before release it.

  • My new thoughts about jQuery in Magento

    Today, I have two new thoughts about jQuery in Magento.

    Firstly, I will stop using a jQuery plugin called jQuery corner. It is a great plugin though. The reason of withdraw is jQuery corner plugin rounds elements with any radius I can specify on the fly. Once upon a time I thought dynamic cornering a great feature. However, jQuery UI comes with ui-corner classes. It has fixed value of radius for all round corners. It does not let me round different elements to different radius, but guarantees the consistency. In this case, consistency is much more important than flexibility. When I do want make exceptional larger or smaller radius for some kinds of elements (one or two kinds at most), I can code css just for that class.

    Secondly, I will start a new Magento module project called Jqol. It reads “JQuery themerOLler”, or “JQuery OnLine” (whatever you feel easier to remember). I am inspired by how easy it is to use jQuery Themeroller to build and change the look and feel of a website. It is a pity Magento does not come with jQuery. I keep mention it because I can not live without jQuery (for now). I implemented jQuery the very first thing when I started diving into Magento. While jQuery is getting stronger and more popular, I would like it play more important role in Magento. Therefore, I will let jQuery Themeroller control the look and feel for the top menu, buttons, accordion, tabs, etc. I can imagine a lot of benefits from this module: cleaner code, more semantic, and I (or even visitors to my sites) can switch the jQuery themes easily.

  • Magento adminhtml getUrl

    $this->getUrl($route) can be used in templates to generate an absolute URL based on route, but it only works for frontend URLs. As for adminhtml URLs, unless you say No to “Add Secret Key to URLs” in System >> Configuration >> Admin, a valid adminhtml URL should have a secret key.

    $this->getUrl($route, $params) can take ‘_current’ as one of parameters, but it only attaches current URL’s parameters. Adminhtml URL secret keys vary with controller and action names. So how to get unique secret key by controller and action names? Easy peasy, just use:

    Mage::getSingleton('adminhtml/url')->getSecretKey($controllerName, $actionName);
    

    How to get URL to adminhtml route (attach the secret key automatically if it is enabled)? Just use:

    Mage::helper('adminhtml')->getUrl($route);
    
  • Double quotes or single quotes inside html tag, which one is the better?

    If you google “Double quotes or single quotes inside html tag, which is better”, you might hear someone is talking about using double quotes in html won’t pass XHMTL validation. I think it is nonsense.

    Both of these characters are valid for XHTML, i.e. both

    <tag att="value">
    

    and

    <tag att='value' >
    

    are valid. However, I have a good reason to encourage you to stick to double quotes.

    When value is taken from user input, it is a must to convert all applicable characters to HTML entities by htmlentities(), and htmlentities() default flag is ENT_COMPAT which convert double quotes but leave single quotes alone. That is to say,

    If I use

    <tag att="(value taken from user input)">
    

    constantly in templates, and if htmlentities() converts all double quotes in user input, whatever user input is, it will not break html output.

    I used to use double quotes and single quotes interchangeably, but I learned from Magento. Magento sticks to using double quotes. Its htmlEscape() bridges to htmlspecialchars() but it does not let you pass in a flag to control how to convert double quotes or single quotes. So, if you use

    <tag att='(value taken from user input)'>
    

    , and if you rely on Magento htmlEscape() to convert special charcters for you, when user input has some single quotes, it ruins html output. I am sure you do not want it happens, so stick to using double quotes, always.