手记6:改造Gitment

本文所述的修改请参考我在Fork Gitment项目的基础上改造的Gitmint:Gitmint: a mint on gitment

接下来我们要大力修改Gitment,打造一个『好用那么一点点儿』的博客评论。

11. 改造Gitment

折腾到现在,我们有了一个在Github pages上的博客,以及一个用来写本地博客的Ghost,以及一些相关的工具,例如makesite.sh在这里)。

最后,我们还有了一个第三方提供的支持PHP+HTTPs的空间,这个空间只用来放Gitment的服务端,并且这个服务器事实上也只做一下API的CORS转发而已。

注:你可能已经留意到我并没有告诉大家『到底哪个免费主页空间支持PHP+HTTPS』啊。是的,我刻意隐藏了这个信息,以免文章公开后导致滥用。我最终使用的这个主页空间……老实说,是相当相当不错的了,能给的人家都给了。所以真不忍心被那些挂马的搞死。所以能藏着就藏着吧,如果你想要申请它的话,你直接在我的网站的源码中去找找就好了。

所以接下来,我开始改造Gitment,再一次强调,Gitment真正是个相当NB的项目。我fork了一个版本出来,修改版的在这里:Giment updates by aimingoo

11.1 更有效地支持HTTP/HTTPS的Github Pages

我们前面说过Github pages也分别支持HTTP和HTTPS两种协议,如果你有幸得到一个HTTP的Github pages site——真的很有幸了,得是以前创建的仓库,新仓库已经没有这个选择了——那么,你仍然可以将intersect(在这里)部署在一个仅支持HTTP的Web站点上。

也就是说,只能选择Github pages和intersect同时支持HTTPS,或者反过来选择同时不支持。有趣的是,在Github pages使用HTTP的情况下,Github同时也允许访问者通过HTTPS协议来访问你的主页。这样一来就带来了一个问题:在你的Github oAuth Application后台配置中,你只能设置一个callback地址。

所以我在Gitment中加了一个名为force_redirect_protocol的选择,它会在调用

https://github.com/login/oauth/authorize

时强制redirect_uri参数使用与Github oAuth Application后台配置一致的值,这样才能在HTTPS/HTTP网站上同时通过authorize验证,并且最后总是使用redirect_uri所设置的协议下的地址。

// Update (Getment Proj)/src/gitment.js

// Github setting of 'Authorization callback URL' in your OAuth application
const force_redirect_protocol = 'https'
...

class Gitment {
  ...
  get loginLink() {
    const oauthUri = 'https://github.com/login/oauth/authorize'
    const redirect_uri = this.oauth.redirect_uri || window.location.href.replace(/^https?/i, force_redirect_protocol);
    ...

11.2 使Gitment支持intersect

intersect这个PHP项目(在这里)并没有完全地实现API Geteway特性。比如说,当它转发一个POST请求时,如果你需要添加新的数据到Request,那么就需要根据POST data的不同类型(Content-Type)来决定如何修改:在JSON中添加一个字段,或在不同的encode data中添加一个数据等等;并且还要正确的修改Content-Length。

所以考虑到简单,intersect只支持在GET请求中,或在使用form-urlencoded协议的POST请求中添加数据。例如我们要追加client_secret这个参数并传送到后端,那么就该选用POST协议。

考虑到Gitment实现XHR请求时的特殊性(它专门实现了一个ajaxFactory),我在修改Gitment项目时更要求:

如果使用POST方法,并强制要求使用form-urlencoded协议,那么应该在http.post()方法中传入字符串格式的data,而不能传入对象。

接下来的实现就比较简单了(修改utils.js中的Ajax接口):

// Update (Getment Proj)/src/utils.js, es6 syntax

function ajaxFactory(method) {
  ...
  // 在accept头中加上form-urlencoded支持
  req.setRequestHeader('Accept', '..., application/x-www-form-urlencoded')

  // 在Response数据的解码中支持form-urlencoded
  req.addEventListener('load', () => {
    ...
    if (/urlencoded/.test(contentType)) {
       data = req.responseText ? Query.parse(res) : {}
       ...

  // 强制Requestr的POST协议对string data使用form-urlencoded
  if (method !== 'GET' && method !== 'DELETE') {
    if (isString(data)) {
      body = data
      req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
    ...

第二步(修改Gitment.js):

// Update (Getment Proj)/src/gitment.js, es6 syntax

class Gitment {
  constructor(options = {}) {
    ...
    const { client_id, client_secret, proxy_gateway } = this.oauth

    ...
    var login = !proxy_gateway
      ? http.post('https://gh-oauth.imsun.net', {code, client_id, client_secret}, '')
      : http.post('/login/oauth/access_token', `code=${code}&client_id=${client_id}`, proxy_gateway);

    login.then(data => {
      this.accessToken = data.access_token
      ...

第三步(Gitment创建时Options):

// 现在在Gitment创建时的Options中就可以使用proxy_gateway选项了
//	- 保持了与旧的client_secert的兼容,二选一配置即可
const gitment = new Gitment({
  id: 'Your page ID', // optional
  owner: 'Your GitHub ID',
  repo: 'The repo to store comments',
  oauth: {
    client_id: 'Your client ID',
    proxy_gateway: 'https://your_intersect_gateway'
    // client_secret: 'Your client secret, either this or proxy_gateway',
  },
  // ...
  // For more available options, check out the documentation below
})

11.3 多语言支持

Gitment的作者没有提供多语言支持是觉得『不必要』,因为Github的用户要是这几个单词也不认得大概就只能回家卖烤红薯了。但我的问题在于,我打算做一个多人博客,所以博客的读者还真有不少与Github无关。

所以,我得需要一个多语言支持。

简单的做法,就是写一个这样的translator:

// save as (Getment Proj)/src/translator.js
export function english(Text) {
    return Text;
}

export function chinese(Text) {
    return ({
        'Issue Page': '所有评论',
        'Initialize Comments': '初始化本文的评论页',
        ...
    }[Text]||Text);
}

export default english;

这里用的都是ES6的语法。这几个简单的export说明当前模块导出了english/chinese等等名字,是用来作为提供多语言支持的翻译函数——在不同语言的函数中添加对照表来支持更多的内容。

使用的时候也挺简单的,在Gitment的src/theme/default.js模块中将它装载进来:

// 注:如果使用英文(不翻译),那么以下两种导入方法是等效的
//	import { english as $ } from '../translator'
//  import $ from '../translator'
import { chinese as $ } from '../translator'
...

然后在需要多语言的地方使用下面的代码即可:

// 例如,原始代码
//	issueLink.innerText = 'Issue Page'
// 改成:
issueLink.innerText = $('Issue Page')

一处处地找到,改完就Ok啦。

注意:Gitment使用了ES6的语法,所以有些地方是用ES6的模板字符串的,这些地方要使用类似${$('english text')} 这样的方法来转换。

其它

comment @2017.10.16

Gitmint后来采用了升级过的多语言实现方案,以方便在其它模块系统中集成之(并进一步地支持浏览器语言检测)。这基本上只需要在new Gitmint的options中传入一个'lang'参数就可以了。

参见:https://github.com/aimingoo/gitmint/blob/master/README.md#multi-languages-in-gitmint

Gitmint对Gitment的‘id’字段做了一些特殊的修正,以处理Gitment在“使用location.href”作为id时的一个Bug。这主要是由于url中带有?#带来的。

Gitmint发布了一个对hexo-theme-next友好的更新,主要的特性可以参考这里:https://github.com/iissnan/hexo-theme-next/pull/1919