Author: 芳草苑主

  • 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

  • What is web 2.0?

    Web 2.0 这个名词都出来好多年了。它还是新鲜名词的时候,我买了本书 Web 2.0 strategy guide,太深奥,扔在那里没看。还已有人嚷嚷着 web 3.0,我现在再来讨论“什么是 Web 2.0”似乎有点过时。

    迟了这么多年谈这个,跟我不是搞理论的有关。我倒不是说理论不重要,恰恰相反,理论很重要。我总结不出理论,只能关心我要怎么做才能 web 2.0。当然了,名词也不重要,2.0、3.0 当然就更不重要了,就说怎么做一个好网站吧。

    话题的起因是今天看到一个论坛帖:几月几日某地到某地回程空车,找想搭车的。那个论坛纯属杂谈,突然冒出这个让我有点想笑,转念一想其实没什么好笑的——我22号要去 Heathrow 接老婆,也可以找搭车出程的。但我不屑于在那论坛上发帖,一是不看好那坛的人气,二是都 web 2.0 了,肯定有更好的办法。于是调查了一下,果然有,叫 liftshare。

    唉,我怎么早先没想到呢?几年的独自上下班本来也可以 share 的嘛。唉,这年头只有想不到,没有做不到的。唉,题外话。

    玩了一下 liftshare,感觉还不错。只是它在路线、时间的匹配上不是那么智能,毕竟 liftshare 的算法不是 IBM Watson,我提醒自己。liftshare 到底干了什么呢?它的卖点就是提供一种匹配互补行程的功能,网站不做任何内容,内容是行程,而行程都是用户提供的。

    联想到 YouTube,网站也不做内容,内容是 video,而 video 都是用户上传的。而且,不上传 video 的用户也可以参与做一些既是内容又不是内容、半内容半功能的东西,比如说 playlist。YouTube 有成千上百的有关 jeopardy Watson 的 video,良莠不齐,我看了 Watson 的三天比赛 6 段 video,是不同的人上传的 。我留意了一下,还没有一个人完整上传过这 6 段视频。所以我把它们组织成一个 playlist,方便我介绍给朋友们看。YouTube 在其中干了什么?要说它什么都没干也可以,因为 video 不是它的,playlist 也不是它的,要说它什么都干也可以,因为没有它用户什么也干不了。这就是 web 2.0。

    联想到 SEO,“Content is the King” 被奉为经典,做了内容还不够,还要做原创内容,搞得象我这样的人疲于奔命。其实搜索引擎鼓吹内容的重要性是带有很强的功利性,因为它不做内容,再没人做内容的话,它还靠什么吃饭啊。搜索引擎就像我排 playlist,排得好就有人看,排得不好就没人看,但归根结底要有内容可排,这就是 web 2.0。

    Ecommerce 网站目前沦落为内容网站,内容就是产品。内容网站和搜索引擎之间的关系就好比作家和评论家,理论上家家是平等的,事实上平均水平的评论家比平均水平的作家更吃香。在内容网站和搜索引擎的博弈中,通常也是搜索引擎占主导地位,搞得内容网站整天揣摩搜索引擎的心思。例外也有,除非 ecommerce 网站卖一个全球独家产品,还要是热门产品,这下轮到搜索引擎揣摩了。但这种情况太少了。

    换位思考一下,同是网站,凭什么 ecommerce 网站就是作家,搜索引擎就是评论家?凭的是 web 2.0?可 ecommerce 网站毕竟还是要卖产品的,内容不能不做,那就考虑一下兼做 web 2.0。产品比较单一怎么办?比如外卖店就几个炒菜怎么做 web 2.0(最近在外卖店搭伙,想到的就是炒菜了)?

    我天方夜谭了一下炒菜 2.0 的思路:

    让吃客公布 eatlist,让他们介绍 eatlist 推荐场合,比如情人节、工作日赶场,让别的吃客可以 add eatlist to cart,评选本周/本月/本年最受欢迎 eatlist,奖励 eatlist 原创作者 Android 3.0 手机一部,让他明年用手机点餐。

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

  • Living in the UK

    今天得到感想都是来自生活中的琐事。

    • 因为按揭,我在新的银行开了户。以前没接触,初次合作就发现她能提供一堆比汇丰好很多的服务,比较之下,过去的一个月,汇丰让我损失了 £200 左右(包括多收费和少收益)。
    • Phones4u 还在卖 HTC Wildfire,£129.99 including VAT。隔壁 Carphone Warehouse 在卖 £229.99。大牌就需要这么标价吗?
    • 从 Debenhams 买了件休闲服,was £50, now £25,我花了一分钟试了一下就决定买了。用的是三年前的 gift voucher,刚好面值 £25,付款也很顺利。Gift voucher 给我的印象这玩意一不留神就过期了,虽然三年前我用第一张 gift voucher 时,我就问过 counter 这玩意什么时候过期,她说永不过期。三年后我把第二张 gift voucher 递给 counter 时,我心里还在想,“你敢说过期俺就找经理”?因为咱们吃过很多多变政策的苦了。事实是,counter 拿起 barcode scanner 一扫,就开始 print receipt,中间没多一句话,我很满意。
    • 从 Sainsbury’s 买的便当里的白饭竟然是夹生的。机器化大生产出来的食品连生熟都控制不好,让我实在怀疑 Sainsbury’s 的整体水平。
  • 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>
    

    ?