Author: 芳草苑主

  • phpMyAdmin 2002 error

    最近把家里的测试服务器 Zend Server php 5.2 卸载了,重新安装了 5.3。phpMyAdmin 随着 5.2 卸载也被卸载了。我用

    yum install phpMyAdmin

    重新安装了 phpMyAdmin。在 config.inc.php 设置了必要的参数,但 phpMyAdmin prompted:

    #2002 – The server is not responding (or the local MySQL server’s socket is not correctly configured)

    phpMyAdmin 2002 error
    phpMyAdmin 2002 error

    我浪费了很多时间检查了所有可能的错误,却解决不了问题。最后在 phpmyadmin.net 下载了源码,使用同样的 config.inc.php,顺利登录!

  • Access denied with my own google documents

    Google Apps 404 page
    Google Apps 404 page
    Cannot view pdf in Google Docs
    Cannot view pdf in Google Docs

    如果 google 帐号和 google app 帐号同时登录时,无法查看app docs,除非 app docs 给 google 帐号访问权限,换句话说 google 帐号的登录状态会掩盖 google app 帐号的登录状态。

    如果多个 google app 帐号同时登录,最先登录的域会阻止进入其他域(可以 retrieve docs list,但有时不能打开文档),有些情况下阻止,有些情况下不阻止,情况比较微妙,实在没空多研究。

  • Solve media type differencing after VirtualBox upgrade to 3.2.8

    Virtualbox Disks Inaccessible
    Virtualbox Disks Inaccessible

    我在 VirtualBox 升级到 3.2.8 以后发现其中的 Windows XP Guest OS 无法启动,其他 Guest OS 正常。Windows XP Guest OS 用到的两个 harddisk 文件也显示 inaccessible,而且也 release 不了它们,强行访问还给一个错误信息。

    Medium type of ‘/path/to/harddiskfile’ is differencing but it is not associated with any parent medium in the media registry (‘/path/to/.VirtualBox/VirtualBox.xml’).

    我也没怎么上心,觉得是 VirtualBox 升级版中的 bug,指望在后续版本中把它修复。可是好久没见 Oracle 出更新,只好 google 求帮助。

    我发现跟我有同样遭遇的人还不少,原因或许是 Host OS 使用了 ext4 文件系统导致不兼容。可为什么只影响 Windows Guest OS?管不了那么多,找解决办法要紧。

    解决方案众说纷纭。最后真正解决我的问题的是:

    Step 1: 去下载一个 fix 工具。我下载了 for linux VBoxFixHdd-Linux.tar.gz,其他的去 http://www.virtualbox.org/download/VBoxFixHdd/ 找。

    Step 2: 运行一下
    VBoxFixHdd --filename /path/to/image.vdi --zeroparentuuid

    其中 /path/to/image.vdi 就是 access 不了、release 不掉的问题文件,当然后缀不一定是 vdi,我的是 vmdk(不知道还有别的吗)。

  • CSS styling without images

    I am quite surprised with the styling effect achieved by pure CSS without the help of images. I can round corner and rotate html elements without creating any background images.

    See the result I get.

    Comprehensive styling using pure CSS
    Comprehensive styling using pure CSS

    And this is the ONLY product image in this CSS styling demo:

    Halloin 3D gift bags
    Halloin 3D gift bags

    Although the effect is comprehensive, but with the help of jQuery plugins, the syntax is very simple. Just a few lines of text.

  • SSL, Nginx and Magento

    SSL, Nginx and Magento 这三件东西对我都不陌生。但三件全排在一起,着实挑战了我一下,发生错误是 secure page redirect loop。

    Magento secure page redirect loop
    Magento secure page redirect loop

    原因是 $_SERVER[] 里缺少 HTTPS directive,需要在 fastcgi_params 里添加一行

    fastcgi_param HTTPS on;

    以 Magento 1.4.0.1 为例深究一下—— 在 $_SERVER[‘HTTPS’] 缺失时 app/code/core/Mage/Core/Model/Store.php 的 isCurrentlySecure() 返回值 false,所以 Magento 不停地重定向到 secure url 而不知道当前 url 已经是 secure 了。

    
    public function isCurrentlySecure()
    {
    if (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off') {
    return true;
    }
    
    if (Mage::isInstalled()) {
    $secureBaseUrl = Mage::getStoreConfig('web/secure/base_route_url');
    if (!$secureBaseUrl) {
    return false;
    }
    $uri = Zend_Uri::factory($secureBaseUrl);
    $isSecure = ($uri->getScheme() == 'https' )
    && isset($_SERVER['SERVER_PORT'])
    && ($uri->getPort() == $_SERVER['SERVER_PORT']);
    return $isSecure;
    } else {
    $isSecure = isset($_SERVER['SERVER_PORT']) && (443 == $_SERVER['SERVER_PORT']);
    return $isSecure;
    }
    }
    
    

    我一直觉得用 php 探测当前 protocol 是否为 https 的算法比较“土”,曾经以为会有更好的探测办法,目前看来是没有。不光是没有,而且不可能有。就如有个小秘负责拆信,然后把有用的信纸交给 CEO 阅读,所以 CEO 不可能知道某封信是拿什么信封装的。

  • Magento layout rendering mystery

    今天碰到个问题百思不得其解。起因是我在改进 Magento rcah module 时,想在 category/index/view 输出前修改 head block 里其中一个 item: link_rel。

    具体说来,我想调用 $headBlock->removeItem()->addLinkRel() 把修改过的 link_rel 添加到 head block 的 $_data[‘items’]。由于调用在 renderLayout() 之后,所以 head 在输出时仍然是修改前的 link_rel。这本不奇怪;奇怪的是,我观察到 $headBlock->getCssJsHtml() 在整个过程中只执行了一次,而且是在 $headBlock->removeItem()->addLinkRel() 之后,$headBlock->getCssJsHtml() 应该是跟 html 输出直接相关,为什么在 $headBlock->getCssJsHtml() 看来 link_rel 还是原值?

    Layout rendering 机制对我来说还比较神秘。我只能暂时这么解释:

    renderLayout() 时把所有 block 的状态记住了,所以此后再怎么修改 block 的状态都无济于事。到了输出 html 阶段,所有的输出都是以 renderLayout() 时的 block。

    BTW,在 rcah 模块中,我找到一个非常巧妙的办法来修改 link_rel (canonical link 的值),完全绕过了跟 head block 的纠缠。

  • Magento extension: rcah 0.2.0 is released

    In previous version of rcah module, if “Use Canonical Link Meta Tag For Categories” is enabled in System >> Configuration >> Catalog >> Search Engine Optimizations,

    and if you set a root category as homepage, the homepage html head outputs a link like

    <link rel=”canonical” href=”http://MY_DOMAIN/catalog/category/view/s/URL_KEY/id/ROOT_CATEGORY_ID/” />

    or if you set a subcategory as homepage (System -> Configuration -> General -> Web -> Default Pages -> Default web URL set to “rcah/index/index/id/SUBCATEGORY_ID”), the homepage html head outputs a link like

    <link rel=”canonical” href=”http://MY_DOMAIN/REWRITED_URL” />

    In the first scenario, the canonical link is invalid because Magento native code won’t allow root category page be accessed. In the second scenario, the canonical link is valid, but it should be “http://MY_DOMAIN/” in either scenario.

    Release 0.2.0 is to fix this problem. Download RootCategoryAsHomepage.tar.gz

    Just for your interest, I was going to write IndexController like this to fix the problem.

    /**
    * most code is from CategoryController::viewAction()
    *
    * run _modifyCanonial() before render layout
    */
    public function indexAction()
    {
    if ($category = $this->_initCatagory()) {
    
    Mage::getModel('catalog/design')->applyDesign($category, Mage_Catalog_Model_Design::APPLY_FOR_CATEGORY);
    Mage::getSingleton('catalog/session')->setLastViewedCategoryId($category->getId());
    
    $update = $this->getLayout()->getUpdate();
    $update->addHandle('default');
    
    if (!$category->hasChildren()) {
    $update->addHandle('catalog_category_layered_nochildren');
    }
    
    $this->addActionLayoutHandles();
    
    $update->addHandle($category->getLayoutUpdateHandle());
    $update->addHandle('CATEGORY_'.$category->getId());
    
    
    
    if ($category->getPageLayout()) {
    $this->getLayout()->helper('page/layout')
    ->applyHandle($category->getPageLayout());
    }
    
    $this->loadLayoutUpdates();
    
    $update->addUpdate($category->getCustomLayoutUpdate());
    
    $this->generateLayoutXml()->generateLayoutBlocks();
    
    if ($category->getPageLayout()) {
    $this->getLayout()->helper('page/layout')
    ->applyTemplate($category->getPageLayout());
    }
    
    if ($root = $this->getLayout()->getBlock('root')) {
    $root->addBodyClass('categorypath-'.$category->getUrlPath())
    ->addBodyClass('category-'.$category->getUrlKey());
    }
    
    $this->_initLayoutMessages('catalog/session');
    $this->_initLayoutMessages('checkout/session');
    $this->_modifyCanonial()->renderLayout();
    }
    elseif (!$this->getResponse()->isRedirect()) {
    $this->_forward('noRoute');
    }
    }
    
    /***
    * Find link_rel item in items of head, and modify it
    *
    * Each item is array of 5 in a format like
    * array('type'=>'link_rel',
    *         'name'=>'http://domain/path',
    *         'params'=>'rel="canonical"',
    *         'if'=>null,
    *         'cond'=>null);
    */
    protected function _modifyCanonial() {
    if ($headBlock = $this->getLayout()->getBlock('head')) {
    $items = $headBlock->getData('items');
    foreach ($items as $key => $value) {
    if (false !== strpos($key, 'link_rel')) {
    if ($value['type'] == 'link_rel' && $value['params'] == 'rel="canonical"') {
    $value['name'] = Mage::getUrl();
    $items[$key] = $value;
    $headBlock->setData('items', $items);
    break;
    }
    
    }
    }
    }
    return $this;
    }
    

    However, just before release, I found a much better way: just use setCategoryUrl in _initCatagory(). So now IndexController is now as:

    protected function _initCatagory()
    {
    Mage::dispatchEvent('catalog_controller_category_init_before', array('controller_action'=>$this));
    $rootCategoryId = Mage::app()->getStore()->getRootCategoryId();
    $categoryId = (int) $this->getRequest()->getParam('id', $rootCategoryId);
    if (!$categoryId) {
    return false;
    }
    
    $category = Mage::getModel('catalog/category')
    ->setStoreId(Mage::app()->getStore()->getId())
    ->load($categoryId)
    //force url as a homepage url to work with canonical link
    ->setUrl(Mage::getUrl());
    
    if ($categoryId == $rootCategoryId) {
    //do nothing. bypass canshow test for root category
    }
    elseif (!Mage::helper('catalog/category')->canShow($category)) {
    return false;
    }
    Mage::getSingleton('catalog/session')->setLastVisitedCategoryId($category->getId());
    Mage::register('current_category', $category);
    try {
    Mage::dispatchEvent('catalog_controller_category_init_after', array('category'=>$category, 'controller_action'=>$this));
    } catch (Mage_Core_Exception $e) {
    Mage::logException($e);
    return false;
    }
    return $category;
    }
    
    public function indexAction() {
    parent::viewAction();
    }
    
  • Install SSL certificate on Nginx step by step

    Although I have installed SSL certificates for several times, every time I google step by step guide. So now I take my own note:

    Step 1: Generate key

    $ openssl genrsa -des3 -out verisign-mydomain.co.uk.key 1024
    Generating RSA private key, 1024 bit long modulus
    ………………….++++++
    …………………++++++
    e is 65537 (0x10001)
    Enter pass phrase for verisign-mydomain.co.uk.key:
    Verifying – Enter pass phrase for verisign-mydomain.co.uk.key:

    Step 2:  Remove pass phrase from key (otherwise Nginx asks for pass phrase every time it starts)

    $ openssl rsa -in verisign-mydomain.co.uk.key -out verisign-mydomain.co.uk.no-passphrase.key
    Enter pass phrase for verisign-mydomain.co.uk.key:
    writing RSA key

    Step 3: Generate CSR

    $ openssl req -new -key verisign-mydomain.co.uk.no-passphrase.key -out verisign-mydomain.co.uk.csr
    You are about to be asked to enter information that will be incorporated
    into your certificate request.
    What you are about to enter is what is called a Distinguished Name or a DN.
    There are quite a few fields but you can leave some blank
    For some fields there will be a default value,
    If you enter ‘.’, the field will be left blank.
    —–
    Country Name (2 letter code) [GB]:
    State or Province Name (full name) [Berkshire]:E Sussex
    Locality Name (eg, city) [Newbury]:Brighton
    Organization Name (eg, company) [My Company Ltd]:Euro Ltd
    Organizational Unit Name (eg, section) []:
    Common Name (eg, your name or your server’s hostname) []:mydomain.co.uk
    Email Address []:

    Please enter the following ‘extra’ attributes
    to be sent with your certificate request
    A challenge password []:
    An optional company name []:

    Step 4: Send CSR to authority and get CRT

    Step 5: Download CA intermediate certificate from authroity

    Step 6: Concatenate CA intermediate certificate with CRT (CRT on top)

    $ cat ca.crt >> my.crt

    Step 7: Make necessary changes to Nginx configuration

    Nginx only need two file: my.crt (concatenated) and verisign-mydomain.co.uk.no-passphrase.key

  • Magento custom module frontend url is forced to https. Why?

    If secure url is enable, sometimes Magento custom module frontend url is forced to https but that’s not what I am intended to. I look into it and find it happens when it uses an identical router frontName both for <frontend> and <admin>.

    The module etc/config.xml is like this:

    
    <?xml version="1.0" encoding="UTF-8"?>
    <config>
    <modules>
    <Mynamespace_Mymodule>
    <version>0.1.0</version>
    </Mynamespace_Mymodule>
    </modules>
    <frontend>
    <routers>
    <mymodule>
    <use>standard</use>
    <args>
    <module>Mynamespace_Mymodule</module>
    <frontName>mymodule</frontName>
    </args>
    </mymodule>
    </routers>
    </frontend>
    <admin>
    <routers>
    <mymodule>
    <use>admin</use>
    <args>
    <module>Mynamespace_Mymodule</module>
    <frontName>mymodule</frontName>
    </args>
    </mymodule>
    </routers>
    </admin>
    </config>
    
    

    If frontend router is changed to something different from admin router (change both frontName and routers xml tag name), then the frontend is not redirected to https. If only frontName is different but routers xml is identical, then {{store url=”router_xml_tag/controller/action”}} results an admin frontName; if only routers xml is different but frontName is identical, then frontName is still forced to https.

    What if a module does require a secured connection for <frontend> without <admin>? Just add <secure_url> tag inside <frontend> tag. A sample like this:

    
    <?xml version="1.0" encoding="UTF-8"?>
    <config>
    <modules>
    <Mynamespace_Mymodule>
    <version>0.1.0</version>
    </Mynamespace_Mymodule>
    </modules>
    <frontend>
    
    <secure_url>
    <mymodule>/mymodule/</mymodule>
    </secure_url>
    
    <routers>
    <mymodule>
    <use>standard</use>
    <args>
    <module>Mynamespace_Mymodule</module>
    <frontName>mymodule</frontName>
    </args>
    </mymodule>
    </routers>
    
    </frontend>
    
    
  • Precisely control modules loading sequence in Magento

    Magento 以 alphabetical 顺序加载 app/etc/modules 里的模块。一般来说,我喜欢在我的模块.xml 前加一个 z,保证我的模块是最后加载的,因为我的模块或许需要去更改原有的布局,那当然得在原有的布局已经生成后才去更改。

    我发现不少人跟我有同样想法——喜欢把他们发布的模块.xml 前加 z,有时还加 zz,甚至 zzz。这样就难为我啦,我倾向于去掉这些强加的 z,毕竟是我在统一调配模块。

    如果我发布我的模块,我倒不喜欢加 z,因为我不喜欢被人加,己所不欲勿施于人。而且,既然是独立发布的模块,应该尽量做到环境无关化。我不可能知道用户已经装有什么模块、将来还会装什么模块,我尽我本分,不管别的模块能否做到环境无关化。

    言归正传。今天我把 jQuery 和一些常用的 jQuery plugins 打了包,放进我的 Msdk (Magento Software Development Kit) 模块。目前 layout 的 msdk.xml 是这样的:

    <?xml version="1.0" encoding="UTF-8"?>
    <layout version="0.1.0">
    <default>
    <reference name="head">
    <!-- add jQuery and plugins -->
    <action method="addJs">
    <script>jquery/jquery-1.4.2.noConflict.min.js</script>
    </action>
    <action method="addJs">
    <script>jquery/jquery.textareaCounter.plugin.js</script>
    </action>
    <action method="addJs">
    <script>jquery/jquery.corner.js</script>
    </action>
    <action method="addJs">
    <script>jquery/jquery.transform-0.6.2.min.js</script>
    </action>
    </reference>
    </default>
    </layout>
    

    接着又发现我其他用到 jQuery 的模块无法使用 jQuery,因为它们按 alphabetical 顺序加载在 msdk 模块之前。这让我头痛。Add block 时有 before, after 参数可以控制插入顺序,addJs 没有优先参数,而我又不愿意修改 app/etc/modules 下各个 xml 文件名去控制模块加载顺序(除非我自个儿用 msdk)。

    继续琢磨,磨出一个两全其美的办法:参考 Magento 的 Eav 模块,在用到 msdk 的模块加载时使用 <depends>。

    <config>
    <modules>
    <Mage_Eav>
    <active>true</active>
    <codePool>core</codePool>
    <depends>
    <Mage_Core />
    </depends>
    </Mage_Eav>
    </modules>
    </config>
    

    我早注意到 <depends> 这个 tag,以前觉得它可有可无(我是拿它跟 yum dependency 机制作比较,Magento depends 无法自动解决依赖,所以觉得它可有可无),没想有这等妙用。