-
表单富文本输入,选择什么方式?
现在几乎所有的网站开发都必须面对一个问题,用户怎么输入带格式的文本呢?最基本的功能包括链接、图片、文字加粗、字体大小颜色等。不同的网站有不同的考虑,有不同的实现。实现得越简单,给用户的门槛就越高。我就尝试从简到繁列一下一些常见的实现方式。 1. textarea里写HTML代码 适用于非常简单的文本,如链接、图片。 例子:豆瓣,Facebook,Flickr 当然了,大部分这样的网站都不会要求你换行都用”<br />”来实现的,文本显示的时候,换行符会被自动转换成HTML的换行。有的网站还会给一些辅助,比如豆瓣的日记编辑器上方有几个按钮,可以帮助生成链接等HTML代码。WordPress的quick tag editor也是这样。 对于开发者来说,几乎没有工作量,只需一个html textarea元素即可解决问题。 然而对于用户的要求就比较高了,他们要想插入链接、图片就必须学一些简单的HTML语法。不过用户及时一点HTML都不懂也没关系,他仍然可以发布内容,只是没有链接、图片而已。像Flickr这样流行的服务,有无数的人为它开发帮助用户输入的浏览器扩展、Greasemonkey脚本等,更降低了输入HTML代码的难度。 2. textarea里写自定义语法 例子:Wikipedia,Google Code,大部分论坛 比较常见的语法有wiki markup,Markdown,以及各种论坛系统常用的BBCode等等。当然了,wiki markup语法的目的不仅仅是为了方便用户输入带格式的文本,它还方便了页面之间的互相链接,但是这里也把它归在这一类。在文本要在浏览器中显示的时候,系统将它翻译成HTML. 用户不需要学习HTML了,但这些语法也需要学习,虽然看起来稍微简单点。和第一种方式一样,许多网站也提供辅助按钮。自定义语法的最大优点在于其安全性,稍后讨论。 Flickr的评论里实际上除了支持一些基本的HTML代码,其实也有自定义输入链接的方式。 3. WYSIWYG编辑器 用户门槛很低,可以实现的效果很复杂。 例子:FCKeditor,TinyMCE,NicEdit 这是国内最常见的方式。国内网站大部分认为用户非常低端,而实际上的确如此。就拿已经够直观的FCKeditor来说,有些用户还是弄不明白插入图片时的一大堆属性都是什么东西,这是可以理解的,你自己使用以下就知道了。所以即使拿来现成的可视化编辑器,你还得好好简化简化。比如插入图片,就输入一个图片地址就够了。不过还真有用户连图片地址是什么都不知道的,那……实在不行就让他上传吧。 使用这些编辑器的缺点是客户端需要启用JavaScript才可以正常使用。另外前两个编辑器非常庞大笨重,二位的目标是打败微软的Word产品,所以我们要考虑好用牛刀来杀鸡是不是合适。 安全性考虑 SQL注入的话题这里就不讨论了。 允许用户提交HTML代码,就带来了潜在的危险。一方面是网站数据的安全,另一方面是页面显示的安全。前几天feelinglucky就披露了yupoo的一个漏洞,尽管这不是由于输入带格式文本造成的,但这是允许HTML代码提交的常见漏洞,危害到网站数据的安全。 如果对用户提交的数据不加处理,还可能会造成显示错乱,比如数据中大量没有关门的标签。如果用户想恶作剧,还可以增加一个元素让它占据整个网页,隐藏所有内容。 千万不要试图仅仅在客户端过滤代码,这就好像敌人要来进攻,你去人家门口堵着一样,敌人很容易绕过这道防线,从后门出来就可以了。服务器端的过滤是绝对必要的。 自定义语法大都不使用尖括号,这给安全性带来了帮助。在服务器端,所有的尖括号均转换成HTML entity表示,然后做语法翻译,这是最简单的方式。 上述一、三方式都是提交HTML代码,服务器端就必须至少过滤JavaScript代码,不能让它在客户端执行。另外还要分析代码的标签是否完整,某些标签是否允许,等等。
-
WordPress Feed添加自定义内容的正确方法
前段时间,Smashing Magazine的文章“10 Useful RSS-Tricks and Hacks For WordPress”第三条讲怎么在RSS Feed中插入任意内容。实际上该hack出自wprecipes,但是很遗憾,它已身在高墙外了。 这种方法就是添加两个filter,分别在the_excerpt_rss和the_content_rss. 貌似合理,但实际上呢?且看wp-includes/feed-rss2.php的关键部分: [code lang=’php’] ]]> ]]> ]]> ]]> [/code] 首先鄙视一下RSS中只输出摘要的blogger,这样我们就只需关心外层if判断的else块了。Feed中的每个item都有一个description标签,调用的是the_excerpt_rss函数。而最重要的是content:encoded这个标签,因为多数RSS阅读器都是根据这里的内容显示的。可以看到,如果文章内容不为空——该条件99.9%的情况下成立,content:encoded的内容就是调用the_content. 该文件输出的是RSS 2.0格式,大部分WordPress blog的默认feed. 再看feed-rss.php, feed-rdf.php和feed-atom.php,只有feed-rss.php输出的RSS 0.92格式的feed没有使用the_content. 因此,按照Smashing Magazine的方法,绝大多数情况下我们添加的内容不会再RSS阅读器中显示。 所以正确的做法是把’the_content’也加上filter: [code lang=’php’]function process_feed($content) { if (is_feed()) { // 只在feed输出时处理,不影响站内内容显示 $content = …; } return $content; } add_filter(‘the_content’, ‘process_feed’);[/code] 最后看一下the_excerpt_rss这个函数到底都干了些什么。它定义在wp-includes/feed.php中,首先调用get_the_excerpt函数,然后应用hook到’the_excerpt_rss’的filter. get_the_excerpt定义在wp-includes/post-template.php中,取得文章的post_excerpt,然后应用hook到’get_the_excerpt’的filter,默认是wp-includes/formatting.php中的wp_trim_excerpt函数。它的作用是当post_excerpt为空的时候(该条件绝大多数情况下成立,我还没见过谁去写那个excerpt——默认在WP文章编辑器下方),取得文章内容,然后应用’the_content’的filter,再做一些去除tag的工作。总结:the_excerpt_rss就是取得the_content的输出,然后去除HTML标签。 我们hook了’the_content’,所添加的内容,自然会进入the_excerpt_rss的输出(只是html tag被抹掉了),所以不要再hook ‘the_excerpt_rss’了,以免造成内容重复!当然对于那些有精力写excerpt的牛人另当别论。 我目前的做法就是将处理函数hook到’the_content’和’the_content_rss’这两个上面。 WordPress这部分现在似乎有点乱,不知道我有没有写错什么。如有错误,请指正,呵呵。
-
Get Recent Comments插件中文乱码问题
前几天启用了Get Recent Comments这个插件,开始的时候一切正常,后来发现有时候中文会出现乱码。 看了一下插件的源代码,发现它用wordwrap这个函数来把很长的单词切分开来。wordwrap函数没有multi-byte的版本,所以它就按字节来数,到了指定的地方就给野蛮地断开了。 在以中文内容为主的blog里,我们基本不需要这个功能。那些超长的url可能会破坏布局,这时只需给外面的容器加上宽度并且overflow:hidden就可以了。无需修改插件源代码,最简单的做法是在该插件的选项页面把Wrap long words at这个选项设置成非常大的整数比如1000. 写下文章标题的时候才去搜索,发现已经有人做了个“中文改良版”,呵呵。
-
Google Reader 可以实时更新feed
大部分在线RSS阅读器都支持XML-RPC ping,不过国内的抓虾、鲜果似乎实际上都不管用。鲜果在feed上右键-反馈,点击马上更新,它才会真正地去抓取。网上有些人说ping了Google blog search,Google reader就随之更新,我没有找到确切的证据。 不过Google Reader的阅读区上方有一个“Refresh”按钮,点击它之后,Google就会马上抓取最新的feed过来,不像鲜果这样的是放入一个队列,可能几分钟之后才能抓回来,这点从侧面反映了Google服务器阵容的强大。 看了一下点击Refresh之后发送的Ajax请求,主要的url就是: https://www.google.com/reader/api/0/stream/contents/feed/http%3A%2F%2Fqingbo.org%2Ffeed?refresh=true 即blog有新文章之后,在浏览器中访问该链接即可使Google Reader更新你的feed,在Google reader里订阅该feed的读者也就看到最新的了,不用等待Google Reader的默认更新周期。其实可以自动化一点,发布文章的时候让wordpress自动去请求该url,或者……不知道放在自动ping的列表里有没有作用,鉴于Google Reader更新的频率已经够快,我就懒得尝试了。 在发布后修改文章的时候,刷新一下Google reader比较好,这样可以让它马上获取更新后的版本。
-
JavaScript 慎用 with
像我这样最初并不是系统学习JavaScript的人,看到with这个东西的第一感觉是兴奋——不用再重复地写“element.style.xxx = …”了,只需要 [code lang=’javascript’]with (element.style) { xxx = …; yyy = …; … }[/code] 但是作为解释型语言,JavaScript都需要在运行时来决定xxx/yyy到底是什么东西。对每一个名字(不仅仅是被赋值的,也包括读取的变量!)它会首先去在with括号里的对象的属性里去寻找,然后才会找局部变量,最后是全局变量。在JavaScript的虚拟机、JIT技术普及之前,这种性能问题需要非常的注意。 正确的做法是给element.style做一个local variable的cache: [code lang=’javascript’]var es = element.style; es.xxx = …; es.yyy = …; …[/code] 这样不仅相对减少了代码大小,同时性能上也比element.style.xxx = … 要好得多。
-
为WordPress 2.7加上鼓励评论功能
Jim Jeffers刚刚发布了他的encouraged commentary脚本,功能非常强大,罗列一些主要的: 在文章中选中一段文字即可在留言中引用 在某条留言中选中一段文字可引用,并且针对该条留言回复 回复留言 列出某留言的所有回复 列出某评论者在该文章的所有留言 在我看来,第一条是最基本的功能也是最重要的。最后两点,对于评论不是太多的blog来说基本是多余,甚至会显得很杂乱。 而WordPress从2.7版本开始内置了对评论回复的支持,因此第二点、第三点也就没太大意义了。 Jim Jeffers说不打算做任何插件,只在github上给出了源代码以及详细的实现步骤,不仅针对WordPress,大部分的blog平台都可以应用。 我就针对WordPress 2.7,简化一下脚本和整个流程,下面是让一个主题支持encouraged commentary的步骤: 将需要的三个JavaScript脚本放在主题的scripts子目录中,保持主题结构清晰。因为只留下上面所列出的第一条功能,所以Jim Jeffers的脚本大大简化——这个压缩包里的是我简化过的。 在header.php中,引入需要的脚本: [code lang=’php’] [/code] 注意不要使用wp_enqueue_script(’jquery’),它没有提供$函数,而Jim Jeffers用到了。 在single.php中,找到调用the_content函数的地方,把这个调用包在一个div中。例如: [code lang=’php’]
-
JavaScript simple dialog
前些天做了一个非常简单的JavaScript library,用来模拟模态对话框(modal dialog),刚刚放到Google code上:javascript-simple-dialog 在一个页面上向用户提示信息、与用户交互有多种方式,最基本的办法就是window的alert和prompt函数。不过alert弹出的同时,会有一声比较恐怖的提示音,prompt在某些浏览器里因为安全限制比较麻烦,用户体验都不是太好。 以前做项目的时候也想找一个现成的对话框来用,但是大部分都不符合我的要求,比如jQuery的插件SimpleModal只能允许同时有一个对话框。jQueryUI的Dialog,当时我记得结构比较复杂,使用的CSS定位比较特殊,对内部内容要求也比较特殊,不知道现在的最新版本怎么样。 于是这次基本是从自己的需求出发,设计并实现了这么一个小东西。最主要的特性是兼容IE(即可以盖住页面上的select按钮——掩饰IE臭名昭著的bug),以及可同时运行多个实例。 更多请看Google code上的项目主页,在那里也可以下载代码或者直接从svn check out. 这里有一个简单的demo.
-
主题支持2.7评论系统需要注意的几点
评论支持嵌套和分页是WordPress 2.7的一大亮点,不过让主题支持这个新的评论系统稍微有点麻烦,WordPress Codex上给出了一长串步骤。 其实最简单的,就是仿照默认主题来做,这里有一篇中文的文章讲得很清楚。 需要注意的是:第三步尤其重要,很多人可能会忘记还要修改header.php,但是如果没有加入这个JavaScript脚本,点击回复就会刷新页面,用户体验很糟糕。 另外如果要做一个通用的主题,就必须支持分页。仿照默认主题,就是下面的代码: [code lang=’php’] [/code] 然而2.7的next_comments_link函数有点问题,在后台启用分页的时候,一切正常。但是禁用分页以后,next_comments_link无论如何都会返回一个链接!这一点用默认主题就可以测试,禁用分页之后,在评论列表的最下方可以看到链接“Newer comments”。我已经在trac里提交了一个bug,希望在下一个版本可以解决。 目前的解决办法是在主题中判断一下page_comments选项,启用时,才生成导航链接: [code lang=’php’] [/code] 我的主题是给自己用的,干脆就直接不生成导航了,因为我现在不打算分页。
-
Firebug 1.3与Ajax response
如果一个Web开发者从来不使用firebug甚至从未听说过,我真替他感到可悲。 昨天,Firebug 1.3正式发布,release notes在Google code上可以看到,现在查找更新即可自动升级了。Firebug是Firefox的扩展,但是它本身也提供了良好的接口,已经有不少扩展了。1.3版本包含了一些对于扩展开发者非常有帮助的特性,然而对于我们普通的Web开发者用户,最期待的更新当属JavaScript debugger了。在一个inner function里单步的时候,你可以通过scope chain来查看function定义同级的变量值。 在调试Ajax应用的时候,用Firebug来查看Ajax请求的response即服务器端返回的响应是最方便的,省了使用HTTP抓包工具。然而从某个时间开始,Firebug的这一功能不再好用了,由于Firefox的一个内部实现问题,插件无法对响应进行分析。从这个讨论来看,应该是2.0版本,不过我记忆中似乎没这么早……症状就是,在console的Ajax请求列表中查看一个的时候,response里并不是服务器返回的数据,而是Firebug的一个提示,具体请看这个网页的第一张图片。要想看到response,Firebug必须重新发送这个请求,这在许多情况下是不现实的。 看到Firebug 1.3发布,我就在console栏里测试了一下Ajax的请求,惊喜地发现response已经可以查看了。 事实上能否查看response数据在于Firefox而不在于Firebug,所以Firebug新版本发布与之关系不是很大。在前面提到的图片中可以看到,Firebug给出了一个链接,指向一个proposal: new nsHttpChannel interface to allow examination of HTTP data before it is passed to the channel’s creator 等这个proposal被实现,查看Ajax response的功能自然就恢复了。从该bug记录下面的评论来看,应该是在3.0.4版本就实现了。再看3.0.4修正的bug列表,果然430155就在里面。 好久没有调试Ajax应用了,这个功能的恢复还是挺好的。
-
Blog全新改版——白板
白板,开会、讨论用的白板。要是麻将里的白板,那下一个主题就是红中了。 主题中用到图片的地方,就尽量模仿成白板上写字的效果。似乎专业的美工做的时候,都是先把整个页面画出来,然后再切割,实现。我很不专业,先是日思夜想,把整个页面画在脑子里,然后就开始实现。 首先画出页面的边框(白板的框架),CSS里就写出header, content, sidebar和footer了。然后就盯着首页,把日志的样式调出来,sidebar的样式,接着把原来主题里的评论样式复制过来改改颜色,中间需要什么图片就随时画(不排除“窃”)。 最后就是最重要的header了。领导亲笔题词,给我开了个好头,让我的心里充满希望: 中间用bittbox做的免费白板字体弄了个类似tag cloud的文字组合,右边是一列订阅按钮。tag cloud里本来没有”RSS”的,后来加上订阅按钮以后,灵机一动,加了个”RSS”,又连了个箭头。一个全新的主题,就这样诞生了。 当然,不可避免地要加上“增强WordPress中的Gravatar应用”一文中介绍的小trick 🙂 接下来,慢慢地加一些实用的插件,改善用户体验。 在做主题的过程中,免不了又跟IE死磕一番。中间也巩固了不少知识,正所谓——越磨砺,越光芒。这些知识都是伟大的IE创造的。有那么两三刻,我都想在主题里针对IE用户放出一个banner:“我谨代表全世界受苦受难的劳苦大众,请求你弃暗投明,放弃史前怪物,使用非IE浏览器。”后来一想,我还得为了这个banner在IE里正常显示而折腾,算了,有空再说吧。 欢迎测试、指正。