现在,我们来实现发表话题、话题页以及评论的功能。首先,我们来实现发表页的功能,修改 default.scheme.js,在适当位置添加如下代码:

"(GET|POST) /create": {
  "request": {
    "session": checkLogin
  }
},
"POST /create": {
  "request": {
    "body": checkCreateBody
  }
}

function checkCreateBody() {
  var body = this.request.body;
  var flash;
  if (!body || !body.title || body.title.length < 10) {
    flash = {error: '请填写合法标题!'};
  }
  else if (!body.tab) {
    flash = {error: '请选择版块!'};
  }
  else if (!body.content) {
    flash = {error: '请填写内容!'};
  }
  if (flash) {
    this.flash = flash;
    this.redirect('back');
    return false;
  }
  body.title = validator.trim(body.title);
  body.tab = validator.trim(body.tab);
  body.content = validator.trim(body.content);
  return true;
}

以上代码用来验证用户状态以及发帖内容是否合法,并对请求体格式化。修改 create.js,添加如下代码:

var Models = require('../lib/core');
var $Topic = Models.$Topic;

exports.get = function* () {
  yield this.render('create');
};

exports.post = function* () {
  var data = this.request.body;
  data.user = this.session.user;
  var topic = yield $Topic.addTopic(data);

  this.flash = {success: '发布成功!'};
  this.redirect('/topic/' + topic._id);
};

最后,将 create.ejs 的内容修改如下:

<% include header %>

<div class="container">
  <div class="ui two column centered grid">
    <div class="right floated left aligned four wide column">
      <% var name = this.session.user.name; %>
      <% include partials/userCard %>
      <% include partials/noReplyCard %>
    </div>
    <div class="left floated  twelve wide column">
      <form method="post">
        <div class="ui form segment">
          <h2 style="margin-top:0">发表话题</h2>
          <div class="field">
            <div class="fields">
              <div class="field required">
                <label>版块</label>
                <div class="ui selection dropdown">
                  <input type="hidden" name="tab">
                  <div class="default text">版块</div>
                  <i class="dropdown icon"></i>
                  <div class="menu">
                    <% $app.tabs.slice(1).forEach(function (tab) { %>
                      <div class="item" data-value="<%= tab %>">
                        <%= tab %>
                      </div>
                    <% }) %>
                  </div>
                </div>
              </div>
            </div>
          </div>
          <div class="field required">
            <label>标题</label>
            <input placeholder="标题字数不少于10字" type="text" name="title">
          </div>
          <div class="field required">
            <label>内容</label>
            <textarea name="content"></textarea>
          </div>
          <input type="submit" class="ui button" value="发布">
        </div>
      </form>
    </div>
  </div>
</div>

<script type="text/javascript">
  $('.ui.dropdown').dropdown();
</script>

<% include footer %>

至此就完成了发帖的功能,接下来实现话题页和评论的功能。修改 default.scheme.js,在适当位置添加如下代码:

"POST /topic/:id": {
  "request": {
    "session": checkLogin,
    "body": checkReplyTopic
  }
}

function checkReplyTopic() {
  var body = this.request.body;
  var flash;
  if (!body || !body.topic_id || !validator.isMongoId(body.topic_id)) {
    flash = {error: '回复的帖子不存在!'};
  }
  else if (!body.content) {
    flash = {error: '回复的内容为空!'};
  }
  if (flash) {
    this.flash = flash;
    this.redirect('back');
    return false;
  }
  body.content = validator.trim(body.content);
  return true;
}

修改 topic/*id.js,添加如下代码:

var Models = require('../../lib/core');
var $Topic = Models.$Topic;
var $Comment = Models.$Comment;

exports.get = function* (id) {
  yield this.render('topic', {
    topic: $Topic.getTopicById(id),
    comments: $Comment.getCommentsByTopicId(id)
  });
};

exports.post = function* (id) {
  var data = this.request.body;
  data.user = this.session.user;

  yield [
    $Comment.addComment(data),
    $Topic.incCommentById(id)
  ];

  this.flash = {success: '回复成功!'};
  this.redirect(this.query.redirect || 'back');
};

需要说明的是,因为在 co 或者 koa 中,yield 后面跟的数组或对象,都是并行执行的,所以我们在传入 topic.ejs 中的两个数据库查询的函数都是并行执行的。同样,添加一条评论以及更新评论数这两个操作也是并行执行的。

修改 topic.ejs,添加如下代码:

<% include header %>

<div class="container">
  <div class="ui two column centered grid">
    <div class="right floated left aligned four wide column">
      <% var name = topic.user.name; %>
      <% include partials/userCard %>
      <% include partials/noReplyCard %>
    </div>
    <div class="left floated  twelve wide column">
      <div class="ui segment">
        <h2 style="margin-top:0">
          <%= topic.title %>
        </h2>
        <div class="topic">
          发布于<%=: topic.created_at | fromNow %> •
          作者 <%= topic.user.name %> •
          <%= topic.pv %> 次浏览 •
          <%= topic.comment %> 个回复 •
          <%= topic.tab %>
        </div>
        <div class="ui divider"></div>
        <div class="content">
          <%-: topic.content | markdown %>
        </div>
      </div>

      <% include comment %>
    </div>
  </div>
</div>

<% include footer %>

修改 comment.ejs,添加如下代码:

<div class="ui segment">
  <div class="ui comments">
    <h3 class="ui header">评论</h3>
    <div class="ui divider"></div>
    <% comments.forEach(function (comment) { %>
      <div class="comment">
        <a class="avatar" href="/user/<%= comment.user.name %>">
          <img src="<%=: comment.user.email | gravatar %>">
        </a>
        <div class="content">
          <a class="author" href="/user/<%= comment.user.name %>">
            <%= comment.user.name %>
          </a>
          <div class="metadata">
            <span class="date">
              <%=: comment.updated_at | fromNow %>
            </span>
          </div>
          <div class="text">
            <%-: comment.content | markdown %>
          </div>
        </div>
      </div>
    <% }) %>

    <% if ($this.session.user) { %>
      <form class="ui reply form" method="post">
        <input type="hidden" name="topic_id" value="<%= topic._id %>">
        <div class="field">
          <textarea name="content"></textarea>
        </div>
        <input type="submit" class="ui button" value="留言">
      </form>
    <% } %>
  </div>
</div>

至此,我们就完成了论坛的所有功能,重启下程序试试吧。