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>

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),以后就正常了。