使用Statiman给博客提供评论功能(二)

 

01 官方文档不可信

Staticman的入门指导不靠谱,按照该指导文件,在Step 1访问url接收合作者邀请的时候一直返回Invitation not found,根本就没办法返回指导文档中说的OK!,原因就是staticmanapp已经是太多项目的合作者了,已经达到了GitHub允许的上限了。

解决这个问题,我先是在自己的局域网搭建了一个Staticman实例,配置明飞的GitHub作为项目合作者,提交用户评论数据。千辛万苦调试通了,但是自己搭的Staticman实例只能局域网内使用,如果给博客使用必须要公共实例,搭建公共实例需要有外部服务器。

后来,我找到了另一个公有的Staticman实例,是由vincenttam提供的,万分感谢。vincenttam是StaticmanLab的维护者,使用Staticman给GitLab提供静态评论功能(其实就是Staticman的V3版本)。他提供的staticman的实例api是https://staticman3.herokuapp.com ,对应的GitHub合作者是staticmanlab

02 官方文档还是不可信

Staticman的入门指导配置项介绍了各个配置项的说明,也给出了配置文件样例,但使用这个样例配置文件在Step 3的时候会报如下报错:

{
    "success": false,
    "data": [
        "allowedFields",
        "branch",
        "format",
        "path"
    ],
    "rawError": {
        "_smErrorCode": "MISSING_CONFIG_FIELDS",
        "data": [
            "allowedFields",
            "branch",
            "format",
            "path"
        ]
    },
    "errorCode": "MISSING_CONFIG_FIELDS"
}

找不到必须的配置项!!!

原因是Staticman的入门指导中给出的public api所用的代码是V2版的Staticman代码(源码的master分支),配置文件样例staticman.sample.yml是给V3版的Staticman用的,不匹配所以出错。

使用master分支搭建出来的Staticman实例可以提供v1、v2版的api,对应的staticman.yml配置文件没有comments。使用dev分支搭建出来的Staticman实例可以提供v1、v2和v3版的api,对应的staticman.yml配置文件有comments。https://api.staticman.net 后端代码是master分支,https://staticman3.herokuapp.com 后端代码是dev分支。(基于2019年02月27日而言,项目代码及分支应该持续更新,您阅读本文时可能已经出现改变)

不知道以上情况的我,使用master分支代码自己搭建Staticman实例,然后是用示例配置文件,一直报错。害我看了半天的代码,坑爹呀!解决办法就是,如果是使用官方实例api(https://api.staticman.net )就将sample中的第5行comments注释掉,如果使用vincenttam的api(https://staticman3.herokuapp.com )就保留第5行comments

Staticman项目规模并不大,逻辑也不复杂,可以多看代码,看源码比看官方指导靠谱多了。

03 还是官方文档不可信

本博客所用的主题是Minimal Mistakes提供的主题,获取源码点这里,主题示例点这里

在Minimal Mistakes的示例主题中有入门指导,其中有介绍评论的地方,里面也说到了从2018年9月开始staticmanapp忙不过来的问题,也介绍了staticman v2和v3的问题。

使用这个主题遇到2个坑:

调试情况下显示不了comments模块

入门指导中写到:

Note: Comments are disabled by default in development. To enable when testing/building locally be sure to set JEKYLL_ENV=production to force the environment to production.

但是我用JEKYLL_ENV=production jekyll build命令就会出现各种404的问题,至今没有解决。我是一直在生产环境调试解决评论问题。。。

当博文的title是中文时,无法显示用户的评论数据

在Minimal Mistakes主题中,跟评论有关的源码文件主要在_includes/comments.html,我的_config.yml配置文件中comments.provider配置的是staticman_v2,所以对应源码的第28行至第101行,其中显示评论的代码是第31行至第47行:

<!-- Start static comments -->
  <div class="js-comments">
    {% if site.data.comments[page.slug] %}
      <h4 class="page__comments-title">{{ site.data.ui-text[site.locale].comments_title | default: "Comments" }}</h4>
      {% assign comments = site.data.comments[page.slug] | sort %}
      {% for comment in comments %}
        {% assign email = comment[1].email %}
        {% assign name = comment[1].name %}
        {% assign url = comment[1].url %}
        {% assign date = comment[1].date %}
        {% assign message = comment[1].message %}
        {% include comment.html index=forloop.index email=email name=name url=url date=date message=message %}
      {% endfor %}
    {% endif %}
  </div>
<!-- End static comments -->

遍历site.data.comments数组,找到key为page.slug的数据,经过调试打印发现,当page.slug为中文时,site.data.comments相应的数据是空字符串。也就是说,不支持中文。

例如,用户给博文华为交换机配置评论,提交评论后,Staticman会在_data\comments文件夹下创建文件夹华为交换机配置,将用户评论数据存到该文件夹的文件comment-{@timestamp}中,例如文件名为comment-1551250624975,调试打印site.data.comments,结果如下:

{
    "": {
        "comment-1551250624975": {
            "_id": "e3cf80e0-3a5c-11e9-b3a5-93bed8986308",
            "message": "测试中文",
            "name": "张洪涛",
            "email": "9dbe00b62ce9224b4df10bbfc6dbe438",
            "url": "",
            "hidden": "",
            "date": "2019-02-27T06:57:04.970Z"
        }
    },
    "Test-EN-Language": {
        "comment-1551234337130": {
            "_id": "f765ce00-3a36-11e9-9e19-03d08e696b02",
            "message": "test",
            "name": "test",
            "email": "9dbe00b62ce9224b4df10bbfc6dbe438",
            "url": "",
            "hidden": "",
            "date": "2019-02-27T02:25:37.129Z"
        }
    }
}

找不到site.data.comments["华为交换机配置"],所以显示不了用户评论。

查找Jekyll中常用变量,需要找到一个文章的唯一标识,且该标识不能为中文。首先我尝试了page.id,但page.id格式为/2008/12/14/my-post/作为文件夹的名称容易产生误解。page.date可能无法保证唯一性。看来默认自带的变量是满足不了需求了。

最后,我在博文的头信息中引入uuid变量,每次在发表博文的时候用uuidgen命令生成唯一标识:

---
title: "使用Statiman给博客提供评论功能(二)"
uuid: 875e22ce-957e-4646-b1fe-ed51de2bd26c
excerpt: "本文主要介绍为了解决本博客的用户评论问题所遇到的各种坑"
last_modified_at: 2019-02-27T16:38:00
comments: true
categories:
  - 技术
tags:
  - Jekyll
---

然后修改提交用户评论数据的代码,_includes/comments.html代码的第49行至第98行:

<!-- Start new comment form -->
  <div class="page__comments-form">
    <h4 class="page__comments-title">{{ site.data.ui-text[site.locale].comments_label | default: "Leave a Comment" }}</h4>
    <p class="small">{{ site.data.ui-text[site.locale].comment_form_info | default: "Your email address will not be published. Required fields are marked" }} <span class="required">*</span></p>
    <form id="new_comment" class="page__comments-form js-form form" method="post" action="{{ site.staticman.endpoint | default: 'https://api.staticman.net/v2/entry/' }}{{ site.repository }}/{{ site.staticman.branch }}/comments">
      <div class="form__spinner">
        <i class="fas fa-spinner fa-spin fa-3x fa-fw"></i>
        <span class="sr-only">{{ site.data.ui-text[site.locale].loading_label | default: "Loading..." }}</span>
      </div>
      <div class="form-group">
        <label for="comment-form-message">{{ site.data.ui-text[site.locale].comment_form_comment_label | default: "Comment" }} <small class="required">*</small></label>
        <textarea type="text" rows="3" id="comment-form-message" name="fields[message]" tabindex="1"></textarea>
        <div class="small help-block"><a href="https://daringfireball.net/projects/markdown/">{{ site.data.ui-text[site.locale].comment_form_md_info | default: "Markdown is supported." }}</a></div>
      </div>
      <div class="form-group">
        <label for="comment-form-name">{{ site.data.ui-text[site.locale].comment_form_name_label | default: "Name" }} <small class="required">*</small></label>
        <input type="text" id="comment-form-name" name="fields[name]" tabindex="2" />
      </div>
      <div class="form-group">
        <label for="comment-form-email">{{ site.data.ui-text[site.locale].comment_form_email_label | default: "Email address" }} <small class="required">*</small></label>
        <input type="email" id="comment-form-email" name="fields[email]" tabindex="3" />
      </div>
      <div class="form-group">
        <label for="comment-form-url">{{ site.data.ui-text[site.locale].comment_form_website_label | default: "Website (optional)" }}</label>
        <input type="url" id="comment-form-url" name="fields[url]" tabindex="4"/>
      </div>
      <div class="form-group hidden" style="display: none;">
        <input type="hidden" name="options[slug]" value="{{ page.slug }}">
        <label for="comment-form-location">Not used. Leave blank if you are a human.</label>
        <input type="text" id="comment-form-location" name="fields[hidden]" autocomplete="off"/>
        {% if site.reCaptcha.siteKey %}<input type="hidden" name="options[reCaptcha][siteKey]" value="{{ site.reCaptcha.siteKey }}">{% endif %}
        {% if site.reCaptcha.secret %}<input type="hidden" name="options[reCaptcha][secret]" value="{{ site.reCaptcha.secret }}">{% endif %}
      </div>
      <!-- Start comment form alert messaging -->
      <p class="hidden js-notice">
        <strong class="js-notice-text"></strong>
      </p>
      <!-- End comment form alert messaging -->
      {% if site.reCaptcha.siteKey %}
        <div class="form-group">
          <div class="g-recaptcha" data-sitekey="{{ site.reCaptcha.siteKey }}"></div>
        </div>
      {% endif %}
      <div class="form-group">
        <button type="submit" id="comment-form-submit" tabindex="5" class="btn btn--primary btn--large">{{ site.data.ui-text[site.locale].comment_btn_submit | default: "Submit Comment" }}</button>
      </div>
    </form>
  </div>
<!-- End new comment form -->

将第77行<input type="hidden" name="options[slug]" value="{{ page.slug }}">修改为<input type="hidden" name="options[uuid]" value="{{ page.uuid }}">

修改staticman.yml配置文件,将配置项path: "_data/comments/{options.slug}"修改为path: "_data/comments/{options.uuid}"

信息:提交Staticman评论的请求中,提交数据中包含fieldsoptions两类变量,fields变量会作为评论数据保存在评论文件中,options作为变量供staticman使用。

经过以上修改,用户提交评论会存在以uuid的文件夹下,那么展示评论的代码对应的site.data.comments[page.slug]也要修改为site.data.comments[page.uuid]

参考资料