Author: 芳草苑主

  • Magento user defined attributes in product list

    Magento内置的产品属性与自定义的产品属性可访问范围不同。默认设置下,自定义的产品属性只能在product view取值,如在product view以外则取不到值;内置的产品属性则没有这种限制。

    为了让自定义产品属性能在Magento前端(比如product list, product new, 或者自己写一个product featured)随意取值,至少有三种方法可以更改默认设置。

    最简单的办法是创建一个config.xml(文件名任意),放在app/etc/modules/下:

    <?xml version="1.0"?>
    <config>
        <frontend>
            <product>
                <collection>
                    <attributes>
                        <attributeyouwant/>
                    </attributes>
                </collection>
            </product>
        </frontend>
    </config>
    

    以后有空再讲其他两种办法。记得一定要刷新Magento cache才能让设置生效!

  • Clear Magento cache is essential after layout xml update

    说来懊恼,为了让Magento product list能够显示自定义属性的值,我查看了magento wiki 和 forum 上几十个帖子,众说纷纭,我也不知道哪个是对的,或者哪个针对当前1.1.8可用。一个个实验下来,排列组合了不下100种可能,比如:

    • 有人说layout add this line: my_attribute,另有人说是my_attribute
    • 有人说要用$_product->getMyAttribute(),也有人说用$_product->getAttribute(‘my_attribute’),也有人说用$_product->getData(‘my_attribute’),也有人说用$_product->getAdditionalData(‘my_attribute’),还有人说用$_product->getResource()->getAttribute(‘my_attribute’)->getFrontend()->getValue($_product))
    • 还有人说Create separate module for your customization, Name it ‘Mycustomization’
      (can be changed).
      Then do this:
      1. Create file app/etc/modules/Mycustomization.xml

      <?xml version="1.0"?>
      <config>
      <modules>
      <Mycustomization>
      <active>
      true
      </active>
      <codePool>
      local
      </codePool>
      </Mycustomization>
      </modules>
      </config>
      

      2. Create folder app/code/local/Mycustomization and inside it app/code/local/Mycustomization/etc/

      3. Create file
      app/code/local/Mycustomization/etc/config.xml

      <?xml version="1.0"?>
      <config>
      <modules>
      <Mycustomization>
      <version>
      0.0.1
      </version>
      </Mycustomization>
      </modules>
      <frontend>
      <product>
      <collection>
      <attributes>
      <designer/>
      </attributes>
      </collection>
      </product>
      </frontend>
      </config>
      

      Once done on the frontend in all product collections you will have designer attribute loaded automatically.

    • 还有Create a file app/etc/modules/category_product_attributes.xml with the following:
      <?xml version="1.0"?>
      <config>
      <frontend>
      <product>
      <collection>
      <attributes>
      <attributeyouwant/>//This is your attribute
      </attributes>
      </collection>
      </product>
      </frontend>
      </config>
      
    • 等等

    最后发现所有组合都不起作用,除非清除magento cache 让 layout xml 生效。

  • Email encoder for not being spidered

    我用Email encoder很久了。以前总是去别人网站上运算一下,拿来就用。如今觉得加密算法很多,我应该筛选一下,并作一个适合我自己的常用的工具。

    因为跟javascript 字符串加解密有关,我首先找到一段程序,试运行了一下,相当不错,据说还支持中文字符串加密以及中文密码。

    function Encrypt(str, pwd) {
    if(str=="")return "";
    str = escape(str);
    if(!pwd || pwd==""){ var pwd="1234"; }
    pwd = escape(pwd);
    if(pwd == null || pwd.length <= 0) {
    alert("Please enter a password with which to encrypt the message.");
    return null;
    }
    var prand = "";
    for(var i=0; i<pwd.length; i++) {
    prand += pwd.charCodeAt(i).toString();
    }
    var sPos = Math.floor(prand.length / 5);
    var mult = parseInt(prand.charAt(sPos) + prand.charAt(sPos*2) + prand.charAt(sPos*3) + prand.charAt(sPos*4) + prand.charAt(sPos*5));
    var incr = Math.ceil(pwd.length / 2);
    var modu = Math.pow(2, 31) - 1;
    if(mult < 2) {
    alert("Algorithm cannot find a suitable hash. Please choose a different password. \nPossible considerations are to choose a more complex or longer password.");
    return null;
    }
    var salt = Math.round(Math.random() * 1000000000) % 100000000;
    prand += salt;
    while(prand.length > 10) {
    prand = (parseInt(prand.substring(0, 10)) + parseInt(prand.substring(10, prand.length))).toString();
    }
    prand = (mult * prand + incr) % modu;
    var enc_chr = "";
    var enc_str = "";
    for(var i=0; i<str.length; i++) {
    enc_chr = parseInt(str.charCodeAt(i) ^ Math.floor((prand / modu) * 255));
    if(enc_chr < 16) {
    enc_str += "0" + enc_chr.toString(16);
    }else
    enc_str += enc_chr.toString(16);
    prand = (mult * prand + incr) % modu;
    }
    salt = salt.toString(16);
    while(salt.length < 8)salt = "0" + salt;
    enc_str += salt;
    return enc_str;
    }
    
    function Decrypt(str, pwd) {
    if(str=="")return "";
    if(!pwd || pwd==""){ var pwd="1234"; }
    pwd = escape(pwd);
    if(str == null || str.length < 8) {
    alert("A salt value could not be extracted from the encrypted message because it's length is too short. The message cannot be decrypted.");
    return;
    }
    if(pwd == null || pwd.length <= 0) { 
    alert("Please enter a password with which to decrypt the message.");
    return;
    }
    var prand = "";
    for(var i=0; i<pwd.length; i++) {
    prand += pwd.charCodeAt(i).toString();
    }
    var sPos = Math.floor(prand.length / 5);
    var mult = parseInt(prand.charAt(sPos) + prand.charAt(sPos*2) + prand.charAt(sPos*3) + prand.charAt(sPos*4) + prand.charAt(sPos*5));
    var incr = Math.round(pwd.length / 2);
    var modu = Math.pow(2, 31) - 1;
    var salt = parseInt(str.substring(str.length - 8, str.length), 16);
    str = str.substring(0, str.length - 8);
    prand += salt;
    while(prand.length > 10) {
    prand = (parseInt(prand.substring(0, 10)) + parseInt(prand.substring(10, prand.length))).toString();
    }
    prand = (mult * prand + incr) % modu;
    var enc_chr = "";
    var enc_str = "";
    for(var i=0; i<str.length; i+=2) {
    enc_chr = parseInt(parseInt(str.substring(i, i+2), 16) ^ Math.floor((prand / modu) * 255));
    enc_str += String.fromCharCode(enc_chr);
    prand = (mult * prand + incr) % modu;
    }
    return unescape(enc_str);
    }

    转念一想,我把问题搞复杂了,对自己没什么好处。我的Email encoder用得着这么复杂吗?毕竟我想保护的又不是名人的Email address。所以我改用我自己的方式——采用双重url encode,在第二重url encoding时,encode全部字符(含字母和数字)。

  • 让浏览主控服务器从幕后走到台前

    可以用nbtstat -a (computer name or ip address) 命令查看该计算机是否浏览主控服务器。如果是,则会出现“..__MSBROWSE_.”字样。

    浏览主控服务器不一定非是选举产生,是可以指定的。

    只要在希望成为浏览主控服务器的电脑上打开注册表,在[HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/Browser/Parameters]一项中把IsDomainMaster的值改为“True”即可。这样,这台电脑就会成为一个浏览主控服务器。这台电脑启动时,如果网络中已经有了浏览主控服务器,那么它会发起一个新的选举,在这个选举中它常常会赢得选举。当然,如果其它电脑也做了同样的设置,那么它们之间的竞争就要按正常的选举规则了。

    另:如不想让某台电脑永远不成为浏览主控服务器,可以在注册表中的[HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/Browser/Parameters]一项中把MaintainServerList的值改为“NO”,即可,这样这台电脑将不会成为浏览列表提供者,同时该电脑的Computer Browser服务也将无法启动,如强行启动会出现错误提示。

  • My view of Liverpool

    在我到过的英国城市中,我最喜欢利物浦。利物浦号称欧洲的文化名城,我只在利物浦停留过一天,还没时间探究她的文化底蕴。我说我喜欢,说白了,就是喜欢她的外观。如果在英国投资物业,如果不考虑工作因素,我首选利物浦。为什么我把利物浦推崇到如此高度?我自己也觉得奇怪,仔细想来,大概是因为我喜欢上海,在英国这么年,到过的城市也不少了,没见过哪个城市有点上海气息,利物浦是个例外。

    为了证明我的感觉是正确的,我归纳一下:

    • 利物浦也是个港口城市,构局也有点像上海,一条默西河(Mersey)仿似黄浦江把利物浦分为东西两部分,目前是利物浦东比利物浦西热闹,毕竟利物浦东位于内陆。
    • 阿尔伯特码头(Albert Dock)可以隔江远眺利物浦西。阿尔伯特码头于1846年由阿尔伯特亲王主持开港典礼而得名。该码头由约克郡设计师杰斯·哈特利设计,占地约2.8公顷,所属仓库用于储存来自远东地区的茶叶、丝绸、烟草和烈性酒。经旧城改造,阿尔伯特码头现已发展为旅游商业区,位列利物浦首要旅游景点,十足的上海外滩。
    • 利物浦步行街规模庞大,英国其他城市的步行街根本无法与之相比。利物浦步行街在历史建筑群基础上往外扩建,我把它分外新旧两部分,新的部分像徐家汇,时尚而尖端;旧的部分仿佛上海南京路,可以充分满足购物和欣赏建筑的双重愿望。

    我事后了解到1999年10月18日,上海和利物浦结为友好城市,我衷心希望她们世世代代友好下去。

    (两个月前拜访的利物浦,现在才写完观感,惭愧)

  • No deplicate posts any more in WordPress

    刚用上WordPress后,我发现WordPress一个不足之处——同一个post_id的post,可以用不同的url来访问。

    以此blog为例,我设置permanent link structure为/%post_id%/%postname%/。我认为post_id查询最快,postname又是meaningful string,最有利seo,所以我把两者结合起来了(后来据说顶层目录的文件最利于seo,所以我更喜欢设置permanent link structure为/%postname%_%post_id%.html,但据说Google不吃这一套,那我就没必要为一些小引擎去劳师动众修改现有的blog,扯远了)。

    当permanent link structure存在post_id和postname两个参数时,post_id起决定作用。早期版本会完全忽略postname,造成只要post_id是正确的,任何杜撰的postname都会被承认。设想一下,如果我发布了一个post以后修改了postname,搜索引擎读到不同url上的相同内容,肯定会降低对此blog的评价。

    所以,当时我想改一下WordPress程序逻辑,当post_id存在但url中的postname不等于数据库中的postname时,返回404页。但是我还没来得及做这个修改,今天惊喜发现WordPress 2.6 (可能更早的版本已经实现了这个修改) 已经会把/(post_id)/(incorrect_postname) 301跳转到 /(post_id)/(postname),这应该比我设计的方案更完好,我非常喜欢WordPress的体贴。

  • Update proof way to customise Magento code

    如果直接修改Magento的文件,Magento每次升级后,又要重新修改,所以这肯定不是个办法。以我最近一次修改/app/code/core/Mage/Core/Model/Email/Template.php为例,修改后Magento使用gmail smtp server来发送邮件。以下我使用update proof way重作一遍。(当前Magento版本是1.1.8)

    1. 不直接修改/app/code/core/Mage/Core/Model/Email/Template.php,而是新建一个/app/code/local/(MyNameSpace)/Core/Model/Email/Template.php,其中(MyNameSpace)可以随意命名。

    /app/code/local/(MyNameSpace)/Core/Model/Email/Template.php 是 /app/code/core/Mage/Core/Model/Email/Template.php的子类,其中我只重载了send方法,所以,/app/code/local/(MyNameSpace)/Core/Model/Email/Template.php不需要拷贝其他部分,它完整的内容是:

    class (MyNameSpace)_Core_Model_Email_Template extends Mage_Core_Model_Email_Template {
    public function send($email, $name=null, array $variables = array())
    {
    if(!$this->isValidForSend()) {
    return false;
    }

    if (is_null($name)) {
    $name = substr($email, 0, strpos($email, ‘@’));
    }

    $variables[’email’] = $email;
    $variables[‘name’] = $name;

    $mail = $this->getMail();
    if (is_array($email)) {
    foreach ($email as $emailOne) {
    $mail->addTo($emailOne, $name);
    }
    } else {
    $mail->addTo($email, ‘=?utf-8?B?’.base64_encode($name).’?=’);
    }

    $this->setUseAbsoluteLinks(true);
    $text = $this->getProcessedTemplate($variables, true);

    if($this->isPlain()) {
    $mail->setBodyText($text);
    } else {
    $mail->setBodyHTML($text);
    }

    $mail->setSubject(‘=?utf-8?B?’.base64_encode($this->getProcessedTemplateSubject($variables)).’?=’);
    $mail->setFrom($this->getSenderEmail(), $this->getSenderName());

    $config = array(
    ‘ssl’ => ‘ssl’, //optional
    ‘port’ => 465, //optional – default 25
    ‘auth’ => ‘login’,
    ‘username’ => ‘your_email’,
    ‘password’ => ‘your_password’);

    $transport = new Zend_Mail_Transport_Smtp(‘smtp.gmail.com’, $config);

    try {
    $mail->send($transport); //add $transport object as parameter
    $this->_mail = null;
    }
    catch (Exception $e) {
    return false;
    }

    return true;
    }
    }

    2. 新建一个/app/code/local/(MyNameSpace)/Core/etc/config.xml,内容是

    <?xml version=”1.0″?>
    <config>
    <modules>
    <(MyNameSpace)_Core>
    <version>1.0.0</version>
    </(MyNameSpace)_Core>
    </modules>
    <global>
    <models>
    <core>
    <rewrite>
    <email_template>(MyNameSpace)_Core_Model_Email_Template</email_template>
    </rewrite>
    </core>
    </models>
    </global>
    </config>

    据说因为Zend Framework的一个bug,tag <email_template>必须在一行内封闭,其间不得有多余的white space。我试过,确实如此。

    3. 最后,在Magento注册本次重载:新建一个/app/etc/modules/(MyNameSpace)_Core.xml,内容是:

    <?xml version=”1.0″?>
    <config>
    <modules>
    <(MyNameSpace)_Core>
    <active>true</active>
    <codePool>local</codePool>
    </(MyNameSpace)_Core>
    </modules>
    </config>

    我只是以重载一个model class为例,重载block class也是同样步骤。按Magento的说法,重载controller class有点区别,我尚未亲试。

  • Send mail using Gmail SMTP in Magento

    我能想到的也是大部分人能想到的——我想用gmail smtp server代替magento内置的sendmail,这么做的好处是用gmail sent mail log all emails sent out by magento。gmail smtp server是我所知的唯一一个能记录发送邮件的smtp服务器(感谢google)。

    Magento默认使用MTA发送邮件,后台configuration可以设置使用smtp发送邮件,但仅限于未使用ssl的smtp服务器,看来只能修改程序才能让Magento用上gmail ssl smtp server了。

    似乎有很多人有这个想法但无法成功完成程序的修改。我参照了Use any smtp to send email (even gmail)的代码,但发现它无法连接gmail smtp server (可能以前可以,但gmail smtp server改了设置?)。

    经我修改,在magento 1.1.8版测试可行的代码方案是:

    修改/app/code/core/Mage/Core/Model/Email/Template.php, 找到public function send($email, $name=null, array $variables = array()),删掉此函数中最后几句:

    try {
    $mail->send(); // Zend_Mail warning..
    $this->_mail = null;
    }
    catch (Exception $e) {
    return false;
    }
    return true;

    替换为:

    $config = array(
    'ssl' => 'ssl',
    'port' => 465,
    'auth' => 'login',
    'username' => 'your_email',
    'password' => 'your_password');

    $transport = new Zend_Mail_Transport_Smtp('smtp.gmail.com', $config);

    try {
    $mail->send($transport); //add $transport object as parameter
    $this->_mail = null;
    }
    catch (Exception $e) {
    return false;
    }
    return true;

    除此之外无需任何其他修改。赶紧试一下吧。

    原贴主要的误导之处是一个错误的$config:

    $config = array(
    'ssl' => 'tls',
    'port' => 465,
    'auth' => 'login',
    'username' => 'your_email',
    'password' => 'your_password');

    其实,如果非要使用tls的话,相应的port应该是587。tls+587的搭配虽然能发邮件,但编码不对,我想应该在程序其他地方相应调整。这个我就没去研究了,ssl+465已能正常发送邮件,我的想法已经实现了。

  • abuse and postmaster as email list

    Google help有提到:

    For your convenience, the Google Team monitors abuse@yourdomain.com and postmaster@yourdomain.com to ensure that we can properly address all reports of spam, abuse, and technical issues. If you’d like a copy of messages sent to abuse@yourdomain.com and postmaster@yourdomain.com, create email lists for both addresses, and add yourself as a recipient.

    Since abuse and postmaster are reserved aliases, you won’t be able to use them as usernames or nicknames.

    可我在读到这段说明之前,就建了两个email lists: abuse@mydomain.com 和 postmaster@mydomain.com,我并未想着要把这两个特殊地址创建为单独的邮箱用户,因为我觉得这样做有悖Google App Mail Best Practices。看来我和google想到一起去了。

  • 为什么局域网内某些电脑只能凭IP访问,不能凭名字访问

    为什么同样在一个局域网内,某些电脑能在网上邻居里显示出来,某些电脑就显示不出来?在A电脑上能看到某台电脑,在B电脑上就看不到它?某台电脑能凭IP访问(\\192.168.x.x),却不能凭名字访问(\\pcname),但是凭IP访问过一次以后就可以凭名字访问了?

    这些怪现象我已经碰到得多了,差不多见怪不怪了。今天让我偶然中发现其中的原因(防火墙因素除外):如果被访问电脑设为静态IP(是指手动配置被访问电脑网卡IP,非通过DHCP服务器MAC绑定得来的固定IP),而且访问电脑TCP/IP协议属性->高级->WINS->NetBIOS禁用的话,那就会出现上述现象。