Author: qingbo

  • Gravatar简介

    现在介绍这个似乎有点晚了,许多人都在用这个服务,不过我有点小用……

    Gravatar(globally recognized avatar)是一个很不错的想法,用户在该网站上传头像图片之后,就可以被任何网站使用。只要用户在其它网站提供了email地址,这些网站就可以知道用户头像图片的地址。当然了,email地址并不会暴露在页面上,它是用email的md5 hash来确定头像地址的。

    由于它十分流行,2007年10月18日被automattic收购。现在,wordpress已经内置了gravatar的支持,一切都是很自然的事情——当初Gravatar的流行,也依赖了wordpress广大的用户基础。

    对于一般网站的开发者来说,现在头像对于用户是必不可少的一个功能。然而头像的上传、剪切、存储并不是小菜一碟。如果网站决定使用gravatar,那就会省了这道工序。更重要的是,对于一个起步中的网站,可能愿意主动上传头像的用户不会超过10%,因为在硬盘上浏览图片,找到合适的头像不一定是一件易事,而用户还没有确定他是否会在这个网站活跃。

    所以gravatar对于开发者和用户都有帮助。如果你在wordpress搭建的blog(本blog即是)里留言发现别人的头像很cool而你的是默认,很有可能是人家使用了gravatar,赶紧去gravatar上传一张头像吧。

  • 迎接2009,Blog主题更新

    其实没更新啥,WordPress 2.7的评论功能大大增强,主要是增加了comment threading功能,而这一功能以前大部分blogger都是用插件Brian’s Threaded Comments实现的。我今天就抽了一点时间,去掉了插件,换用WordPress本身的wp_list_comment函数来显示评论。

    晚上吃饭的时候,电视上都是迎接新年的气氛。而我回顾2008年,却感觉有点失败,一事无成。以后要把更多的时间用在工作和学习上了,尽量不做无聊的事。作为兴趣爱好,我会多看看PHP以及WordPress的东西。最近如果有时间,就写几篇关于WordPress 2.7的评论功能的文章吧。

  • 啥时候<!–more–>有用?

    今天打开自己的blog,发现那两个flash游戏自动播放很烦,我一时写文章不勤快了,就没有把它们埋下去,每次打开首页都听那么一遍。

    于是<!–more–>派上了用场。把两篇文章分别编辑了一下,给flash嵌入的代码前面加上了more,这下首页没有了。话说,多亏我今天把电脑的静音取消了,要不还不知道用户体验这么差呢……

    文章特别长的时候,这个特殊标签也非常有用,它会让blog的首页高度不会出奇地大。尤其是文中有大量图片的时候,前阵子从黄山回来,那些流水帐里不少就用了more标签。

    首页默认是10篇文章,缩小每篇文章的高度,有利于读者更快地发现更多有意思的东西。

    另一方面,首页的数据量也大大减小,页面加载速度大大加快,也提升不少用户体验。

  • 慎用StringEscapeUtils.escapeHtml方法

    几个月前写了篇文章推荐使用Apache commons-lang的StringUtils来增强Java字符串处理功能,我也一直在项目中大量使用StringUtils和StringEscapeUtils这两个实用类。

    最近在数据库里发现某个表的内容全都成了HTML entity表示,中文也全被转换成了”&#25105;”这样的格式,而在页面上显示一切正常。最终发现造成这个后果的原因是在将字符串保存到数据库之前,用StringEscapeUtils.escapeHtml对其进行了处理。

    字符串过滤是在许多Web应用开发中需要考虑的问题。比如用户输入一个左尖括号(<),在输出HTML代码对其进行显示的话,应该用”&lt;”来表示(而我为了正常显示这句话中出现的两次尖括号,必须做多余的工作,你可以看一下源代码)。是在用户将表单提交后就做转换呢,还是在从数据库取出内容向用户展示时再转换?这个问题需要仔细考虑。在入库前转换,看似一劳永逸,然而如果用户需要编辑这个内容,你需要原路退回,做个逆向的转换,这看起来比较麻烦。所以大部分的转换都是在从数据库中取出数据之后做的,struts的bean:write标签的filter属性就是控制这个开关的(默认为true)。

    而某些内容是不会被编辑的,比如一般的电子邮件,这类就可以在用户提交表单之时转换,一劳永逸。于是我使用了StringEscapeUtils.escapeHtml来做这个工作。

    如果是英文内容,没有任何问题,然而它将一个中文字符转换成了8个字符。在UTF-8的编码中,每个汉字占用三个字节,每个英文字符占用一个字节。这样的转换造成了大量的空间浪费。

    简单的办法是自己写一个escapeHtml方法,替换左右尖括号就可以了。虽然还有一些HTML entity未被转换,可能造成输出的网页不符合web标准,但是大部分浏览器都可以正常显示也就够了。

  • Firefox必备扩展 – Copy URL +

    在网上看到有趣的新闻,经常有在IM上分享给朋友的冲动。不过这个操作一般都要复制两次,一次标题,一次链接,这样才专业。

    不用想就知道肯定有人做了这样的扩展,Copy URL +就是一个,我不知道有没有其它的,至少这个已经足够好用了。

    遗憾的是作者似乎已经不再维护了,官方版本只支持Firefox 1.5. 没关系,下面的Review提供了支持3.x的版本:http://www.utm.edu/staff/bmoseley/copyurlplus-v1.3.4.xpi

    安装后在网页的右键菜单上有一个Copy URL+的菜单组,下面默认有三个子菜单项(没有选中文本时仅显示第一个):

    Copy URL + Title -> 复制链接和标题,中间用回车隔开
    Copy URL + Selection -> 复制链接和选中文本,中间用回车隔开,并且选中文本加引号
    Copy URL + Title + Selection -> 上面二者之结合

    该扩展的功能比想象的还要强大,可以在Firefox的profile文件夹里的user.js中定义新的菜单项(Windows在C:\Documents and Settings\[username]\Application Data\Mozilla\Firefox\Profiles\[randstr])。自定义的方法在扩展的README文件中有介绍(解压或安装后可以看见),也可以在扩展的官方主页看到。

    对于像我这样不喜欢使用可视化编辑器写东西(更利于控制HTML代码质量)的blogger,用这个插件来生成链接代码要方便多了。我定义了两个菜单项如下:

    user_pref(’copyurlplus.menus.1.label’, ‘Copy URL + Title (HTML)’);
    user_pref(’copyurlplus.menus.1.copy’, ‘<a href=”%URL%”>%TITLE%</a>’);
    user_pref(’copyurlplus.menus.2.label’, ‘Copy URL + Selection (HTML)’);
    user_pref(’copyurlplus.menus.2.copy’, ‘<a href=”%URL%”>%SEL%</a>’);

    第一个把标题作为链接文本,第二个把选中文本作为链接文本,生成对应的HTML代码。

  • 打企鹅变速齿轮版

    新鲜出炉,该快的快,该慢的慢,节约时间,为构建和谐社会做贡献:


    版权归yetisports.org所有。。

    另,我打出了5556的成绩,不过是用正规版本打出的,估计自己也难以超越了:

    5556

  • 打企鹅 – 神奇的非洲 修改版

    这两天打企鹅打上瘾了,我玩的是神奇的非洲这个版本,应该是最耐玩的一个吧。注意第一棒要从长颈鹿的肚子下面过去,这样有机会越过两棵树,一下打1200米左右,某同学甚至打出了1500米,真是……我的最好成绩是5棒4000多米。

    让人不爽的是这个游戏没有一个reset键,有时候明明失误了,很希望重新来过,只能靠点浏览器的刷新按钮来完成。于是再听一遍烦人的音乐,再点好多次OK.

    痛定思痛,不是很熟悉flash的我还是把这个难题给攻克了。本来想加个reset的button的,后来图简单,就把5棒结束后乌鸦衔着的那个记分牌给搬过来了,哈哈,效果很明显:


    就加了那么五六行action script. 因为机器上没有一个字体,原来的字体被替换了,不过没多大影响。

  • SVN 错误 – no such revision

    Linux服务器不小心断电,重启后,修复了硬盘,但是SVN Server出问题了。在浏览器中访问,看似一切正常,但是要check out,update的时候,就报错 no such revision ‘xxx’,xxx是一个版本号。

    对repository目录执行svnadmin verify,发现很早之前的版本还在,从某个版本号就出错了。Google了一通之后,发现搜索结果里和我的症状都不太相同。丢失的太多了,近期备份也没有,难道就重新建一个repository了事?

    不过很快找到了问题所在,db/revprops目录中的文件从0开始,到了出错的版本号就没了。打开这些文件,发现是纯文本,格式如下:

    K 10
    svn:author
    V 7
    someone
    K 8
    svn:date
    V 27
    2008-07-08T19:54:09.605509Z
    K 7
    svn:log
    V 9
    log here.
    END

    显然,一个key(K)跟一个value(V),大写字母K、V之后是字符串的长度,revprops中的文件只是记录了每个版本提交的信息。

    由于我们同时使用了trac,这些revprops文件丢失并没有损失,trac的数据库中都有保存,可以通过它来重新生成。但是为了简便起见,我就把最后一个存在的版本号文件复制了N遍:

    for i in $(seq 250 500)
    do
     cp 249 ${i}
    done

    250到500是缺失的版本号,249是最后一个存在的。就这样终于把版本库给修复了。

  • 补记凤凰岭秋游

    一个多月前我们去了一次凤凰岭,两男两女。原计划是十点上山,灌点泉水,两三点下山,然后去小汤山泡汤。

    结果上山后不久,看到旁边有一条比较宽阔的山路,我们就抛弃了正途,沿这条山路向上走了。旁边出现一条小溪,一个水池子里有好多蝌蚪,某些没见过世面的同志看得很开心——“咦,这个长后腿了耶!”“啊,这个有四条腿还有尾巴哎!”漫山遍野散步的红色,也让人很兴奋,想到后来我还到香山去看了红叶,哪里有这么好啊。

    站在山脚 红叶

    沿南线走了不久遇到的一个岔路口成了这天行程的转折点,队伍一番商量之后,决定逢路口即选择右边的。后来证明,右倾机会主义害人不浅。在经过几次右倾之后,小溪离我们越来越远,路越来越不清晰,最后几乎找不到了。有时候是高大的灌木挡着,需要弯腰钻过去,或者把稍微矮小一点的用力踩到下面。有时候是大片大片的藤,差不多一人高,人就像在一张蛛网上爬一样,倒是很安全。

    藤

    在山谷的时候,看着上面不远处就是几块巨石,似乎山顶并不远,不过往上爬的过程中才发现,那是很难爬上去的,我们只能斜向上,往更高的山顶爬。很快,最初的那些巨石就在我们下面了。

    光秃秃的岩壁

    最后终于到了一个小山顶。没有任何准备,没有任何专业的装备,几乎没有食物和水,我们就爬上来了。因为路一直比较难走,甚至有的地方很危险,我们一直不想回头,想着前面的路可能会比较好走,前面也会有好的景色。那些高大的灌木和藤条,向下走的话,估计会更困难,所以我们决定从另一面下山。人大概都是这样,不知道前面的路是不是更危险,反正不想走回头路了。走到山顶那两块巨石前面,看到了不久前有人留下的痕迹,稍稍地宽慰了一些。毕竟这不是无人区,我们还能看到人的痕迹,远处就是北京市,还能听见对面山上有人说话的声音。

    山顶 公交队也上来过

    下山的路一开始就不好走,好几次需要从巨石上滑下来,主要是有两位女士。不过很快发现一条平整的小路,我们一下就兴奋起来,心想又找到路了,顺着它一定能走下山去。路况相对于乱石杂草那是好多了,但是它把我们引向了一个悬崖。悬崖边上有浓密的灌木,多亏我当时速度不是特别快,否则一下就走下去了。拽着藤条向下看,不是特别高,不过因为有女的,我们就退回去另选它路了。右边是一条山涧,堆满了从山上滚落的大石块,我们决定从那里走。过去以后发现那里也有人走过的痕迹,小心地下到谷底,又在一处石壁上发现了公交队的“到此一游”——果然,他们也是从这里下来的。这时看到前方的巨石附在悬崖上,仿佛一只乌龟在向上爬。

    公交队也上来过 乌龟

    到了谷底终于有了真正的路,心情也就放松下来了,到处都是山核桃树,山核桃落得满地都是,我们用帽子装了不少。在山顶比较饿的时候,就是吃了几个山核桃充饥,味道很纯正。半小时后走到了南线的大路上,身心疲惫,回头望下来的山,如果事先知道,肯定不会爬上去的……路过一家采摘的园子,尝了一下我平时从来不吃的苹果,真好吃,买了一些带回家,结果第二天吃的时候,完全没有了下山时品尝的美味……

    回头看我们下山的路

    这一路真可谓是披荆斩棘,多少有点后怕。至于泉水,下次再灌吧。

  • Greasemonkey与DOMContentLoaded

    昨天在hexy要求下写了一个小脚本,在豆瓣的邀请页面加了全选按钮。

    按照习惯,我把处理DOM的代码放在了窗口的load事件的处理函数中:

    window.addEventListener('load', function(e) {
    	...
    }, false);
    

    但是hexy说页面上有很多图片(头像)的情况下,页面一显示出来没有全选那个checkbox,延迟好一会之后才出现。以前处理的页面因为比较简单,都没怎么在意这个问题,看来这次是不行了。各种JavaScript库里都有另外的途径来替代”load”,比如jQuery有ready函数,mootools有domready事件。而我们写Greasemonkey脚本,只需要考虑Firefox就好了,它有一个DOMContentLoaded事件,页面上解释得非常清楚:

    Fired on a Window object when a document’s DOM content is finished loaded, but unlike “load”, does not wait till all images are loaded. Used for example by GreaseMonkey to sneak in to alter pages before they are displayed.

    我们要的就是它。然而在把load替换为DOMContentLoaded之后,我发现更糟了,代码根本不执行了。一番搜索之后,我发现我从来就没仔细想过一个问题,Greasemonkey的脚本到底是什么时候执行的?这个页面解释了这个问题。

    原来Greasemonkey的脚本就是在DOMContentLoaded这个事件触发后执行的,而这个事件对每次打开网页只有一次,脚本中再往这个事件添加处理函数当然是徒劳了!于是解决方案就是更简单地,把处理代码外面的皮剥掉,直接放出来就可以了。结果就是现在的这个样子

    可能是最初学写脚本的教程里就是教用load事件的,看起来又比较专业,我就一直这么用了,可见启蒙教育是多么重要,要不是这次教训,可能我就一辈子这么缺钙了。

    实际上一般情况下对DOM的操作都不需要等到load事件,DOM结构完整之后就可以了,这样给用户的体验也会好很多。