Category: 小小草

IT 技术领域学海无涯。其实任何领域都学海无涯,无非 IT 发展太快了,让我有更多嘘唏。希望我掌握的技术有如小小草,虽然渺小,却有旺盛的生命力。

  • Magento Cush module is about to release

    Long long time ago, I installed Customshippingrate module, but never made it work on my site. The module sits on the disk but disabled. Recently I took some trouble and about three days developing my own module Cush for admin panel users to charge special shipping price when creating an order.

    Cush module does not override any Magento classes so it is virtually 100% compatible with other modules. Neither does Cush module override any Magento templates so basically it reuses Magento native interface. Cush is not a shipping method so it can work with any Magento shipping methods or 3rd party shipping methods.

    Cush module injects customisation logic by javascript. What it actually customise are values in magento_sales_flat_quote_shipping_rate where is pool of shipping quotes. Values customised belong to an individual quote so the customisation hits its point.

    When writing Cush module, I found a defect in Magento own code app/design/adminhtml/default/default/template/sales/order/create/shipping/method/form.phtml.

    
    echo $this->getCarrierName($_rate->getCarrier());
    
    

    The above code reads value from configuration and shows it as carrier title. If I was not injecting shipping method customisation logic, I would not realise it is a defect. Carrier title value, like method title and shipping rate, if read from magento_sales_flat_quote_shipping_rate using $_rate->getCarrierTitle(), will make more sense. Values from configuration will not reflect carrier title changes done by Cush. I respect Magento own templates, especially adminhtml templates. Instead of overriding this template, I wrote some additional javascript in Cush module to correct carrier title. I stick to my green principle although it costs much more time developing.

    Upon installing Cush module, I removed all files of Customshippingrate module. A small accident happened – can not create shipment. It turns out during creating shipment, Magento is gathering all config paths start with “carriers/” and working out all carriers between two slashes. It is a bad logic, or I can call it a bug. It does not check whether this carrier exists or is active. So, very bad.

    Just add to my Magento caveats: after removing a shipping method, remove all entries of “carriers/CARRIER_CODE/*” in magento_core_config_data.

  • Magento datetime picker is not picking up time value

    Magento 用了 dynarch.com 的 calendar 1.0 javascript,有个 bug:无法得到 time 的值。

    dynarch.com calendar 已经是 2.0 了,单独使用的话,能显示和修改 time 的值。

    我暂时没想好该怎么办:我倾向于用 jQuery 去增强 Magento(prototype 我也用不好,其他的就更不要说了),但 jQuery 现下的版本只有 datepicker,官方还没有 datetimepicker。试过很多第三方 jQuery datetimepicker plugin,没觉得某一款有 jQuery 的神韵。

  • I could use Google apps account as my OpenId

    Years ago, I knew OpenId.
    Years ago, I knew Google account supports OpenId as OpenId provider.
    Years ago, I knew Google apps premium version account support SSO.

    And I was misled by many threads that Google apps standard version account does not support OpenId. I did try several times without success, but I did not try hard.

    Today, a great post How to Setup OpenID with Google Apps gave me a big confidence that I can do it. So I tried hard – spent a whole evening trying, and successfully logged into SourceForge with my Google apps acount, and I am using Google apps standard version!

  • Controller override and request rewrite in Magento

    There are three ways to override controller in Magento. They fit for various purposes.

    The first and easiest way can be used to route the request to more than one module. When a request arrives on a frontName, it usually is rounted to a module. For example, /cms/page/view is routed to cms module page controller view action. If I have developed a cms related module called “faq” with a brand new controller “QuestionController”, but I want it share the same cms frontName with cms module, i.e., I want /cms/question/any_action be routed to faq module.

    It is very easy to achieve by a config.xml like the following:

    <config>
    	<frontend>
    		<routers>
    			<cms>
    				<args>
    					<modules>
    						<any_name>MyNamespace_Faq</any_name>
    					</modules>
    				</args>
    			</cms>
    		</routers>
    	</frontend>
    </config>
    

    Strictly speaking, no overriding in above example because it only activates a brand new controller. In a truly overriding example, if I want PageController of faq module override PageController in cms module, I can add before=”Mage_Cms” to make sure PageController of faq module supersede the same name controller in cms module. The complete configuration is shown below:

    <config>
    	<frontend>
    		<routers>
    			<cms>
    				<args>
    					<modules>
    						<any_name before="Mage_Cms">MyNamespace_Faq</any_name>
    					</modules>
    				</args>
    			</cms>
    		</routers>
    	</frontend>
    </config>
    

    The second way can be used to mass override controllers.

    <config>
    	<global>
    		<rewrite>
    			<any_name>
    				<from><![CDATA[#^/cms/#]]></from>
    				<to>/faq/</to>
    				<complete></complete>
    			</any_name>
    		</rewrite>
    	</global>
    </config>
    

    The above configuration makes all controllers in faq module override same name controllers in cms module. In class Mage_Core_Controller_Varien_Front, there is

    $pathInfo = preg_replace($from, $to, $request->getPathInfo());
    

    doing path info string replacement based on regular expression. And because it is based on regular expression, I can do mass replacement at a time.

    The tag flags whether requested path info should be turned into new module path info. In other words, when the request arrives on /cms/page/view, it is rewritten to /faq/page/view. Without tag, the action layout handle is cms_page_view; with tag, the action layout handle is faq_page_view.

    I can use this method to route the request to a brand new controller, i.e.

    <config>
    	<global>
    		<rewrite>
    			<any_name>
    				<from><![CDATA[#^/cms/question/#]]></from>
    				<to>/faq/question/</to>
    				<complete></complete>
    			</any_name>
    		</rewrite>
    	</global>
    </config>
    

    Note that cms module does not have question controller, but the above configuration will not cause any error.

    The third way can be used to override individual actions.

    <config>
    	<global>
    		<routers>
    			<cms>
    				<rewrite>
    					<page>
    						<to>faq/question</to>
    						<override_actions>true</override_actions>
    						<actions>
    							<view_action><to>new_module/new_controller/new_action</view_action>
    						</actions>
    					</page>
    				</rewrite>
    			</cms>
    		</routers>
    	</global>
    </config>
    

    This method is documented in class Mage_Core_Controller_Varien_Action.

    * This will override:
    * 1. cms_module/page_controller/view_action to new_module/new_controller/new_action
    * 2. all other actions of cms_module/page_controller to faq_module/question_module

    It is very handy to precisely control the request rewrite to action level, but it can not route to a brand new controller. The following code will cause an error.

    <config>
    	<global>
    		<routers>
    			<cms>
    				<rewrite>
    					<question><!-- Error: cms module does not have question controller -->
    						<to>faq/question</to>
    						<override_actions>true</override_actions>
    						<actions>
    							<view_action><to>new_module/new_controller/new_action</view_action>
    						</actions>
    					</question>
    				</rewrite>
    			</cms>
    		</routers>
    	</global>
    </config>
    

  • 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 it a serious security leak for WordPress bloggers?

    By WordPress default settings, anyone who knows WordPress blog administrator’s email address can put this address in comment required field, and make comment without being moderated as if he was the administrator. I think it is very bad design of authentication. It worries me a lot although none of my blog users pretend to be me so far.

    I never publish my email address on the web to protect it from spam. But you can find my email address in many places – I print it on business cards, write it on CV, give it to people first met in pubs… Now I have to rethink how to use my email address.

    I think password check is essential if someone is acting like the administrator in WordPress blog. If it can be bypassed, it means I have to keep my email address as my privacy.  Birthday is a privacy, residence address is a privacy. There are so many privacies to keep. And now, email address?

  • jQuery dialog widget moves dialog DOM to the very end of body

    我在 checkout page 上使用 jQuery dialog widget 来显示 terms and conditions。Terms and conditions 的内容不随 checkout 页面加载(因为大部分顾客不会去读它),而在首次点击 Read terms and conditions 链接后以 ajax 获取内容后用 jQuery dialog 展示。再次点击则不必再用 ajax 获取,而直接用 jQuery dialog 展示。

    因为不同种类的产品会对应不同的 terms and conditions,所以 checkout page 会根据 shopping cart 里的所有产品列示出对应的 terms and conditions,可能会有多条。我原想把每条 Read terms and conditions 链接获取的 terms and conditions 内容插入在该链接的后面,在点击事件发生后就可以判断其后的内容 DOM 存在与否决定是否要执行 ajax post 操作。这样可以避免重复获取每个 terms and conditions,以加快用户响应和减少服务器负担。

    可如意算盘打了一通,写出最初稿一运行,发现每点一次 Read terms and conditions,ajax post 都要执行一次。一开始我还以为内容 DOM 插入位置不正确,被判断为没加载过而去执行 ajax post。查了好久才发现,内容 DOM 插入位置是正确的,只是每次执行 jQuery dialog(),它就把 dialog DOM(也就是内容 DOM)移到了 body DOM 的最后。

    这意味着我不能在 Read terms and conditions 链接后寻取对应的内容 DOM,而必须用另外的办法链接和内容的关系。当然办法有很多,这里就不多说了。

  • 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.

  • The trash has reached its maximum size

    The trash has reached its maximum size
    The trash has reached its maximum size

    估计 KDE 用户碰到这个问题的概率比较高。这个问题的起因不明,症状是要删除文件时出现

    The trash has reached its maximum size! Cleanup the trash manually.

    检查垃圾箱里没有东西,却无法删除文件进垃圾箱。直接删除操作不受影响。

    问题的直接原因是 Trash 的 metadata 有误,解决办法是把 $HOME/.local/share/Trash 直接删掉,然后执行一下 Empty Trash 操作(其实是让 Trash 重建 metadata),以后就正常了。

  • Learned something basic

    今天又长知识了。

    首先,最简单的,更正了长久以来的想当然,php 下 explode(‘,’, ”) != array(),而是得到长度为 1 的数组,key 是数值 0,value 是空字符串。天哪,我有多少个程序是基于 explode(‘,’, ”) == array() 写下去的,这下影响大了,得好好查一查。

    其次,发现一个不晓得是 sshfs 的 bug 还是 gedit 的 bug。复制错误的过程是:用 Nautilus 或 Dolphin 打开 sshfs 挂载的目录,右击创建一个新文件。文件创建是成功的,属性是 774,用 gedit 打开它却无法保存,提示是没有写权限;但用 kwrite 编辑保存一切正常;用 gedit 再编辑 kwrite 编辑过的文件又能保存。或者,在右击创建一个新文件后,执行一次 chmod 774 filename,也能用 gedit 编辑保存了。

    再次,发现在 IE6 下,用 javascript 增大元素的尺寸(比如 jQuery widget 化,增加 border,增加 padding,等),会增大父元素的尺寸。哪怕父元素已用 css 静态赋以宽度值,宽度也会被改变,这是某些精心布局在标准浏览器下很好看,到了 IE6 就面目全非的一大原因。万恶的 IE6 啊,当然从另一方面,说明精心布局仍不够“精心”。为 IE6 布局好比极限运动,挑战好心情的极限;如果看到下属多花一倍时间 fix for IE6,挑战的也是老板的心理极限。

    然后,发现 jQuery gallery 里有两个 themes (Humanity, Vader) 的参数不太正常,多了 tr 参数,不知道怎么多出来的,删了似乎没影响。

    最后,如果父元素包含所有的子元素都是 float:left 或 float right 的话,不做特殊操作,父元素是没有高度的。父元素的后续元素用一个 clear:both 就能站到该站的位置,但如要为父元素本身画一个边框就稍有难度。最早我用的办法是在这个元素所有的子元素之后增加一个隐形的<div style=”height:0; clear:both;”></div>,但这个硬生生加进去的元素改变了 DOM 结构,破坏了语义,不够 SEO。在 Magento 里学到了另一个方法 :after { clear:both; },如 .clearer 的示例,但要为低版本的 IE 专门写 clear after fix。我维护一份自己的 style.css,override Magento 原版的 style.css。我觉得这个任务就很“繁重”,如果再来一份自己的 style-ie.css,override Magento 原版的 style-ie.css,就为 clear after fix?总觉得小题大作。今天发现一个 clear after all floating children 的 neat solution,就是在父元素上设定 style=”overflow:auto; zoom:1″。overflow 让父元素调整到应有的高度,zoom 也是必须的,否则 IE6…,唉!

    最后的最后,发现 z-index 值在 IE6 下被重置的简单通用的 fix。这个问题的来源是若干个 position 后的元素,给它们设定 z-index,IE6 下根本不按设定值 layout,而且还摸不到规律。比如下拉菜单,有时被其他东西给压住。fix 是赋予下拉菜单 z-index 时,赋予父元素(整个菜单)更高的 z-index。