Category: 小小草

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

  • Is Magento is_in_stock an attribute

    is_in_stock在magento export products时会是其中一列,但我在attributes里怎么也找不到它,它到底是什么?

  • Magento 1.2.0.1 is out

    今天很欣喜地发现Magento 1.2.0出来了,赶紧用magento connect upgrade了一下,结果升级失败,前台出不来,后台也进不去。

    我只好手动安装了一下,原先php环境设置前台safe_mode on,只对后台safe_mode off,安装了magento 1.2.0以后,前台出现了错误提示 Warning: set_time_limit() [function.set-time-limit]: Cannot set time limit in safe mode。magento对php环境要求越来越高了,我只好把safe_mode 前台、后台统统off。这下就没问题了。

    在我成功把magento 1.2.0运行起来后,发现1.2.0.1发布了,才几个小时啊,magento更新真够快了。我猜想1.2.0.1是针对safe_mode苛刻要求发布的小补丁,因为我又把magento 1.2.0更新成1.2.0.1,试着把php safe_mode恢复原先的设置,一切正常!

  • Bank of China

    中国银行的网站终于改进了一下。以前外汇牌价网页在非中文平台上总显示乱码,现在用上了utf-8,这才有点像个”中国银行-全球门户网站”

  • 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服务也将无法启动,如强行启动会出现错误提示。

  • 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想到一起去了。